File metadata

Template author: Morgan Carr-Markell
Template last modified on: Feb 28, 2023
Notebook modified by: Katie Wang
Notebook last modified on: Mar 1, 2023

Source

The code and many descriptions in this notebook come from a tutorial posted on the (National Ecological Observatory Network) NEON website and originally from the Data Carpentries. You can find the original tutorial here: https://www.neonscience.org/resources/learning-hub/tutorials/introduction-working-raster-data-r#toggle-26

Important note: Remember to:

  1. download the data zip file
  2. unzip it
  3. place the unzipped folder in this hw5 directory

Otherwise, you will have no data to work with!

Getting to understand the data

Before you start the tutorial on loading, plotting, and processing raster data in R, go to this NEON tutorial and read just to the end of the section called “Gridded, or Raster, LiDAR Data Products”

  1. What do DTM, DSM, and CHM stand for?
    DTM: Digital Terrain Model.
    DSM: Digital Surface Model.
    CHM = DSM - DTM; it’s the Canopy Height Model.

  2. What are the two main formats of LiDAR data files and how are they different?
    One is the .las file format for LIDAR point clouds. These are collections of points with (x, y, z) values and other attributes, and are not aligned with a particular grid. The other is the gridded or raster data format, which is a grid of cells all of the same size. Each cell represents a certain amount of area on the ground.


Raster 00: Intro to Raster Data in R

Original Authors: Leah A. Wasser, Megan A. Jones, Zack Brym, Kristina Riemer, Jason Williams, Jeff Hollister, Mike Smorul

Original Last Updated: Apr 8, 2021

In this tutorial, we will review the fundamental principles, packages and metadata/raster attributes that are needed to work with raster data in R. We discuss the three core metadata elements that we need to understand to work with rasters in R: CRS, extent and resolution. It also explores missing and bad data values as stored in a raster and how R handles these elements. Finally, it introduces the GeoTiff file format.

00-A Loading libraries

options("rgdal_show_exportToProj4_warnings"="none") # to suppress warning messages
library(raster)
Loading required package: sp
library(rgdal)
Please note that rgdal will be retired during 2023,
plan transition to sf/stars/terra functions using GDAL and PROJ
at your earliest convenience.
See https://r-spatial.org/r/2022/04/12/evolution.html and https://github.com/r-spatial/evolution
rgdal: version: 1.6-4, (SVN revision 1196)
Geospatial Data Abstraction Library extensions to R successfully loaded
Loaded GDAL runtime: GDAL 3.5.2, released 2022/09/02
Path to GDAL shared files: C:/Users/achro/AppData/Local/R/win-library/4.2/rgdal/gdal
GDAL binary built with GEOS: TRUE 
Loaded PROJ runtime: Rel. 8.2.1, January 1st, 2022, [PJ_VERSION: 821]
Path to PROJ shared files: C:/Users/achro/AppData/Local/R/win-library/4.2/rgdal/proj
PROJ CDN enabled: FALSE
Linking to sp version:1.6-0
To mute warnings of possible GDAL/OSR exportToProj4() degradation,
use options("rgdal_show_exportToProj4_warnings"="none") before loading sp or rgdal.
library(rasterVis)
Loading required package: lattice
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.4.0      ✔ purrr   1.0.1 
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.2.1      ✔ stringr 1.5.0 
✔ readr   2.1.3      ✔ forcats 0.5.2 ── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ tidyr::extract() masks raster::extract()
✖ dplyr::filter()  masks stats::filter()
✖ dplyr::lag()     masks stats::lag()
✖ dplyr::select()  masks raster::select()

00-B Loading the data

# Load raster into R
DSM_HARV <- raster("NEON-DS-Airborne-Remote-Sensing/HARV/DSM/HARV_dsmCrop.tif")

# View raster structure
DSM_HARV 
class      : RasterLayer 
dimensions : 1367, 1697, 2319799  (nrow, ncol, ncell)
resolution : 1, 1  (x, y)
extent     : 731453, 733150, 4712471, 4713838  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs 
source     : HARV_dsmCrop.tif 
names      : HARV_dsmCrop 
values     : 305.07, 416.07  (min, max)

00-C Plotting with base R plot function

# plot raster
# note \n in the title forces a line break in the title
plot(DSM_HARV, 
     main="NEON Digital Surface Model\nHarvard Forest")

00-D Types of Data Stored in Raster Format

Raster data can be continuous or categorical. Continuous rasters can have a range of quantitative values. Some examples of continuous rasters include:

  • Precipitation maps.
  • Maps of tree height derived from LiDAR data.
  • Elevation values for a region.

The raster we loaded and plotted earlier was a digital surface model, or a map of the elevation for Harvard Forest derived from the NEON AOP LiDAR sensor. Elevation is represented as a continuous numeric variable in this map. The legend shows the continuous range of values in the data from around 300 to 420 meters.

Some rasters contain categorical data where each pixel represents a discrete class such as a landcover type (e.g., “forest” or “grassland”) rather than a continuous value such as elevation or temperature. Some examples of classified maps include:

  • Landcover/land-use maps.
  • Tree height maps classified as short, medium, tall trees.
  • Elevation maps classified as low, medium and high elevation.

Note: Skipped section on creating categorical plot because it is covered in more detail below

00-E What is a GeoTIFF??

Raster data can come in many different formats. In this tutorial, we will use the geotiff format which has the extension .tif. A .tif file stores metadata or attributes about the file as embedded tif tags. For instance, your camera might store a tag that describes the make and model of the camera or the date the photo was taken when it saves a .tif. A GeoTIFF is a standard .tif image format with additional spatial (georeferencing) information embedded in the file as tags. These tags can include the following raster metadata:

  • A Coordinate Reference System (CRS)
  • Spatial Extent (extent)
  • Values that represent missing data (NoDataValue)
  • The resolution of the data

In this tutorial we will discuss all of these metadata tags.

More about the .tif format:

Note: I am skipping the CRS part of the tutorial because we covered that last week, but if you want a refresher you can look at that section of the online tutorial here

00-F Extent

The spatial extent is the geographic area that the raster data covers.

The spatial extent of an R spatial object represents the geographic “edge” or location that is the furthest north, south, east and west. In other words, extent represents the overall geographic coverage of the spatial object.

00-G Resolution

A raster has horizontal (x and y) resolution. This resolution represents the area on the ground that each pixel covers. The units for our data are in meters. Given our data resolution is 1 x 1, this means that each pixel represents a 1 x 1 meter area on the ground.

The best way to view resolution units is to look at the coordinate reference system string crs(). Notice our data contains: +units=m.

Note: You’ll see a warning about the Proj.4 representation being deprecated. You can ignore that.

crs(DSM_HARV)
Coordinate Reference System:
Deprecated Proj.4 representation:
 +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs 
WKT2 2019 representation:
PROJCRS["unknown",
    BASEGEOGCRS["unknown",
        DATUM["World Geodetic System 1984",
            ELLIPSOID["WGS 84",6378137,298.257223563,
                LENGTHUNIT["metre",1]],
            ID["EPSG",6326]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8901]]],
    CONVERSION["UTM zone 18N",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",-75,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",0.9996,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",500000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",0,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]],
        ID["EPSG",16018]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]]] 

Note: I am skipping the raster min and max values section

00-H NoData Values in Rasters

Raster data often has a NoDataValue associated with it. This is a value assigned to pixels where data are missing or no data were collected.

By default the shape of a raster is always square or rectangular. So if we have a dataset that has a shape that isn’t square or rectangular, some pixels at the edge of the raster will have NoDataValues. This often happens when the data were collected by an airplane which only flew over some part of a defined region.

In the image below, the pixels that are black have NoDataValues. The camera did not collect data in these areas.

# stack() is a raster function to create a rasterStack object
RGB_stack <- stack("NEON-DS-Airborne-Remote-Sensing/HARV/RGB_Imagery/HARV_RGB_Ortho.tif")

# Create an RGB image from the raster stack
par(col.axis = "white", col.lab = "white", tck = 0)
# Note: col.axis and col.lab set the colors for the axis annotation and x/y labels
      # tck is the length of tick marks on the axes

# The code below plots an RGB color image using layers in a rasterStack object
# We have to tell the function which layers in the stack contain red green and blue
plotRGB(RGB_stack, 
        r = 1, 
        g = 2, 
        b = 3, 
        axes = TRUE, 
        main = "Raster With NoData Values\nRendered in Black")

Note: Skipped section on setting NoData values to NA

00-I NoData Value Standard

The assigned NoDataValue varies across disciplines; -9999 is a common value used in both the remote sensing field and the atmospheric fields. It is also the standard used by the National Ecological Observatory Network (NEON).

If we are lucky, our GeoTIFF file has a tag that tells us what is the NoDataValue. If we are less lucky, we can find that information in the raster’s metadata. If a NoDataValue was stored in the GeoTIFF tag, when R opens up the raster, it will assign each instance of the value to NA. Values of NA will be ignored by R as demonstrated above.

00-J Bad Data Values in Rasters

Bad data values are different from NoDataValues. Bad data values are values that fall outside of the applicable range of a dataset.

Examples of Bad Data Values:

  • The normalized difference vegetation index (NDVI), which is a measure of greenness, has a valid range of -1 to 1. Any value outside of that range would be considered a “bad” or miscalculated value.
  • Reflectance data in an image will often range from 0-1 or 0-10,000 depending upon how the data are scaled. Thus a value greater than 1 or greater than 10,000 is likely caused by an error in either data collection or processing.

Find Bad Data Values

Sometimes a raster’s metadata will tell us the range of expected values for a raster. Values outside of this range are suspect and we need to consider than when we analyze the data. Sometimes, we need to use some common sense and scientific insight as we examine the data - just as we would for field data to identify questionable values.

Create A Histogram of Raster Values

We can explore the distribution of values contained within our raster using the hist() function which produces a histogram. Histograms are often useful in identifying outliers and bad data values in our raster data.

# view histogram of data
hist(DSM_HARV,
     main = "Distribution of Digital Surface Model Values\n Histogram Default: 100,000 pixels\n NEON Harvard Forest",
     xlab = "DSM Elevation Value (m)",
     ylab = "Frequency",
     col = "wheat")
Warning: 4% of the raster cells were used. 100000 values used.

Notice that a warning is shown when R creates the histogram.

This warning is caused by the default maximum pixels value of 100,000 associated with the hist function. This maximum value is to ensure processing efficiency as our data become larger!

We can define the max pixels to ensure that all pixel values are included in the histogram. USE THIS WITH CAUTION as forcing R to plot all pixel values in a histogram can be problematic when dealing with very large datasets.

# View the total number of pixels (cells) in is our raster 
totalCells <- ncell(DSM_HARV)

Now we can add that as a parameter, maxpixels:

# create histogram that includes with all pixel values in the raster
hist(DSM_HARV, 
     maxpixels = totalCells,
     main = "Distribution of DSM Values\n All Pixel Values Included\n NEON Harvard Forest Field Site",
     xlab = "DSM Elevation Value (m)",
     ylab = "Frequency",
     col = "wheat4")

Note that the shape of both histograms looks similar to the previous one that was created using a representative 10,000 pixel subset of our raster data. The distribution of elevation values for our Digital Surface Model (DSM) looks reasonable. It is likely there are no bad data values in this particular raster.

00-K Raster Bands

The Digital Surface Model object (DSM_HARV) that we’ve been working with is a single band raster. This means that there is only one dataset stored in the raster: surface elevation in meters for one time period.

A raster dataset can contain one or more bands. We can use the raster() function to import one single band from a single OR multi-band raster. We can view the number of bands in a raster using the nlayers() function.

# view number of bands
nlayers(DSM_HARV)
[1] 1

However, raster data can also be multi-band meaning that one raster file contains data for more than one variable or time period for each cell. By default the raster() function only imports the first band in a raster regardless of whether it has one or more bands.

00-L View Raster File Attributes

Remember that a GeoTIFF contains a set of embedded tags that contain metadata about the raster. So far, we’ve explored raster metadata after importing it in R. However, we can use the GDALinfo(“path-to-raster-here”) function to view raster metadata before we open a file in R.

Note: In this notebook, you’ll need to click through the three data frames to see all of the info about this rasterLayer.

# view attributes before opening file
GDALinfo("NEON-DS-Airborne-Remote-Sensing/HARV/DSM/HARV_dsmCrop.tif")
Warning: GDAL support is provided by the sf and terra packages among othersWarning: GDAL support is provided by the sf and terra packages among othersWarning: GDAL support is provided by the sf and terra packages among others
rows        1367 
columns     1697 
bands       1 
lower left origin.x        731453 
lower left origin.y        4712471 
res.x       1 
res.y       1 
ysign       -1 
oblique.x   0 
oblique.y   0 
driver      GTiff 
projection  +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs 
file        NEON-DS-Airborne-Remote-Sensing/HARV/DSM/HARV_dsmCrop.tif 
apparent band summary:
apparent band statistics:
Metadata:
AREA_OR_POINT=Area 

Notice a few things in the output:

  • A projection is described using a string in the proj4 format : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs
  • We can identify a NoDataValue: -9999
  • We can tell how many bands the file contains: 1
  • We can view the x and y resolution of the data: 1
  • We can see the min and max values of the data: Bmin and Bmax.

It is ideal to use GDALinfo to explore your file before reading it into R.

Note: rgdal is being replaced by functions in sf and terra/stars so this won’t be true in the future.

CHALLENGE: EXPLORE RASTER METADATA

  1. Without using the raster function to read the file into R, determine the following about the NEON-DS-Airborne-Remote-Sensing/HARV/DSM/HARV_DSMhill.tif file:
GDALinfo("NEON-DS-Airborne-Remote-Sensing/HARV/DSM/HARV_DSMhill.tif")
Warning: GDAL support is provided by the sf and terra packages among othersWarning: GDAL support is provided by the sf and terra packages among othersWarning: GDAL support is provided by the sf and terra packages among others
rows        1367 
columns     1697 
bands       1 
lower left origin.x        731453 
lower left origin.y        4712471 
res.x       1 
res.y       1 
ysign       -1 
oblique.x   0 
oblique.y   0 
driver      GTiff 
projection  +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs 
file        NEON-DS-Airborne-Remote-Sensing/HARV/DSM/HARV_DSMhill.tif 
apparent band summary:
apparent band statistics:
Metadata:
AREA_OR_POINT=Area 
  1. Does this file have the same CRS as DSM_HARV?
    Yes; they both have proj=utm.

  2. What is the NoDataValue?
    NoDataValue is -9999.

  3. What is resolution of the raster data?
    The x and y resolution are both 1.

  4. How large would a 1x1 pixel area would be on the Earth’s surface?
    Because res.x and res.y are 1, and the units of the projection system are meters, each pixel is a 1m x 1m area on Earth.

  5. Is the file a multi- or single-band raster?
    This file has 1 band.

Notice: this file is a hillshade, which uses information about elevation to add shadows to make changes in elevation clearer to a map viewer.


Raster 01: Plot Raster Data in R

Original Authors: Leah A. Wasser, Megan A. Jones, Zack Brym, Kristina Riemer, Jason Williams, Jeff Hollister, Mike Smorul

Original Last Updated: Apr 8, 2021

This tutorial reviews how to plot a raster in R using the plot() function. It also covers how to layer a raster on top of a hillshade to produce an eloquent map.

01-A Plot Raster Data in R

In this tutorial, we will plot the Digital Surface Model (DSM) raster for the NEON Harvard Forest Field Site. We will use the hist() function as a tool to explore raster values. And render categorical plots, using the breaks argument to get bins that are meaningful representations of our data.

First, let’s plot our Digital Surface Model object (DSM_HARV) using the plot() function. We add a title using the argument main=“title”.

# Plot raster object
plot(DSM_HARV,
     main="Digital Surface Model\nNEON Harvard Forest Field Site")

01-B Plotting Data Using Breaks

We can view our data “symbolized” or colored according to ranges of values rather than using a continuous color ramp. This is comparable to a “classified” map. However, to assign breaks, it is useful to first explore the distribution of the data using a histogram. The breaks argument in the hist() function tells R to use fewer or more breaks or bins.

If we name the histogram, we can also view counts for each bin and assigned break values.

# Plot distribution of raster values 
DSMhist <- hist(DSM_HARV,
     breaks = 3,
     main = "Histogram Digital Surface Model\n NEON Harvard Forest Field Site",
     col = "wheat3",  # changes bin color
     xlab = "Elevation (m)")  # label the x-axis
Warning: 4% of the raster cells were used. 100000 values used.

Warning message!? Remember, the default for the histogram is to include only a subset of 100,000 values. We could force it to show all the pixel values or we can use the histogram as is and figure that the sample of 100,000 values represents our data well.

# Where are the breaks and how many pixels in each category?
DSMhist$breaks
[1] 300 350 400 450
DSMhist$counts
[1] 31676 67862   462

Looking at our histogram, R has binned out the data as follows:

300-350m, 350-400m, 400-450m

  1. Which elevation bin are most pixels in? There are 67575 pixels in the largest bin, which is 350-400m.

We could specify different breaks, if we wished to have a different distribution of pixels in each bin.

We can use those bins to plot our raster data. We will use the terrain.colors() function to create a palette of 3 colors to use in our plot.

The breaks argument allows us to add breaks. To specify where the breaks occur, we use the following syntax: breaks = c(value1, value2, value3). We can include as few or many breaks as we’d like.

# plot using breaks.
plot(DSM_HARV, 
     breaks = c(300, 350, 400, 450), 
     col = terrain.colors(3),
     main = "Digital Surface Model (DSM)\n NEON Harvard Forest Field Site")

Data Tip: Note that when we assign break values a set of 4 values will result in 3 bins of data.

01-C Format Plot

If we need to create multiple plots using the same color palette, we can create an R object (myCol) for the set of colors that we want to use. We can then quickly change the palette across all plots by simply modifying the myCol object.

We can label the x- and y-axes of our plot too using xlab and ylab.

# Assign color to a object for repeat use/ ease of changing
myCol = terrain.colors(3)

# Add axis labels
plot(DSM_HARV, 
     breaks = c(300, 350, 400, 450), 
     col = myCol,
     main = "Digital Surface Model\nNEON Harvard Forest Field Site", 
     xlab = "UTM Westing Coordinate (m)", 
     ylab = "UTM Northing Coordinate (m)")

01-D Layering Rasters

We can layer a raster on top of a hillshade raster for the same area, and use a transparency factor to created a 3-dimensional shaded effect. A hillshade is a raster that maps the shadows and texture that you would see from above when viewing terrain.

# import DSM hillshade
DSM_hill_HARV <- raster("NEON-DS-Airborne-Remote-Sensing/HARV/DSM/HARV_DSMhill.tif")

# plot hillshade using a grayscale color ramp that looks like shadows.
plot(DSM_hill_HARV,
    col = grey(1:100/100),  # create a color ramp of grey colors
    legend = FALSE,
    main = "Hillshade - DSM\n NEON Harvard Forest Field Site",
    axes = FALSE)

Data Tip: Turn off, or hide, the legend on a plot using legend=FALSE.

We can layer another raster on top of our hillshade using by using add = TRUE. Let’s overlay DSM_HARV on top of the hill_HARV.

# plot hillshade using a grayscale color ramp that looks like shadows.
plot(DSM_hill_HARV,
    col = grey(1:100/100),  #create a color ramp of grey colors
    legend = F,
    main = "DSM with Hillshade \n NEON Harvard Forest Field Site",
    axes = FALSE)

# add the DSM on top of the hillshade
plot(DSM_HARV,
     col = rainbow(100),
     alpha = 0.4, # partly transparent
     add = T, # layers this plot on top of the previous plot
     legend = F)

The alpha value determines how transparent the colors will be (0 being transparent, 1 being opaque). Note that here we used the color palette rainbow() instead of terrain.color().


Raster 02: When Rasters Don’t Line Up - Reproject Raster Data in R

Original Authors: Leah A. Wasser, Megan A. Jones, Zack Brym, Kristina Riemer, Jason Williams, Jeff Hollister, Mike Smorul

Original Last Updated: Apr 8, 2021

Sometimes we encounter raster datasets that do not “line up” when plotted or analyzed. Rasters that don’t line up are most often in different Coordinate Reference Systems (CRS).

This tutorial explains how to deal with rasters in different, known CRSs. It will walk though reprojecting rasters in R using the projectRaster() function in the raster package.

02-A Raster Projection in R

In the Plot Raster Data in R tutorial, we learned how to layer a raster file on top of a hillshade for a nice looking basemap. In that tutorial, all of our data were in the same CRS. What happens when things don’t line up?

Let’s create a map of the Harvard Forest Digital Terrain Model (DTM_HARV) draped or layered on top of the hillshade (DTM_hill_HARV).

DTM_HARV <- raster("NEON-DS-Airborne-Remote-Sensing/HARV/DTM/HARV_dtmCrop.tif")
DTM_hill_HARV <- raster("NEON-DS-Airborne-Remote-Sensing/HARV/DTM/HARV_DTMhill_WGS84.tif")

# plot hillshade using a grayscale color ramp 
#plot(DTM_hill_HARV,
    #col = grey(1:100/100),
    #legend = FALSE,
    #main = "DTM Hillshade\n NEON Harvard Forest Field Site")

# overlay the DTM on top of the hillshade
#plot(DTM_HARV,
     #col = terrain.colors(10),
     #alpha = 0.4,
     #add = TRUE,
     #legend = FALSE)

# I ran this chunk twice and it crashed my R session both times
# It's supposed to only plot the first one

Our results are curious - the Digital Terrain Model (DTM_HARV) did not plot on top of our hillshade. The hillshade plotted just fine on it’s own. Let’s try to plot the DTM on it’s own to make sure there are data there.

Code Tip: For boolean R elements, such as add = TRUE, you can use T and F in place of TRUE and FALSE

# Plot DTM 
plot(DTM_HARV,
     col = terrain.colors(10),
     alpha = 1,
     legend = F,
     main = "Digital Terrain Model\n NEON Harvard Forest Field Site")

Our DTM seems to contain data and plots just fine. Let’s next check the Coordinate Reference System (CRS) and compare it to our hillshade.

# view crs for DTM
crs(DTM_HARV)
Coordinate Reference System:
Deprecated Proj.4 representation:
 +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs 
WKT2 2019 representation:
PROJCRS["unknown",
    BASEGEOGCRS["unknown",
        DATUM["World Geodetic System 1984",
            ELLIPSOID["WGS 84",6378137,298.257223563,
                LENGTHUNIT["metre",1]],
            ID["EPSG",6326]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8901]]],
    CONVERSION["UTM zone 18N",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",-75,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",0.9996,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",500000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",0,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]],
        ID["EPSG",16018]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]]] 
# view crs for hillshade
crs(DTM_hill_HARV)
Coordinate Reference System:
Deprecated Proj.4 representation: +proj=longlat +datum=WGS84 +no_defs 
WKT2 2019 representation:
GEOGCRS["unknown",
    DATUM["World Geodetic System 1984",
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]],
        ID["EPSG",6326]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433],
        ID["EPSG",8901]],
    CS[ellipsoidal,2],
        AXIS["longitude",east,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433,
                ID["EPSG",9122]]],
        AXIS["latitude",north,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433,
                ID["EPSG",9122]]]] 

Aha! DTM_HARV is in the UTM projection. DTM_hill_HARV is in Geographic WGS84 - which is represented by latitude and longitude values. Because the two rasters are in different CRSs, they don’t line up when plotted in R. We need to reproject DTM_hill_HARV into the UTM CRS. Alternatively, we could project DTM_HARV into WGS84.

02-B Reproject Rasters

We can use the projectRaster function to reproject a raster into a new CRS. Keep in mind that reprojection only works when you first have a defined CRS for the raster object that you want to reproject. It cannot be used if no CRS is defined. Lucky for us, the DTM_hill_HARV has a defined CRS.

Data Tip: When we reproject a raster, we move it from one “grid” to another. Thus, we are modifying the data! Keep this in mind as we work with raster data. To use the projectRaster function, we need to define two things:

  • the object we want to reproject and
  • the CRS that we want to reproject it to.

The syntax is projectRaster(RasterObject, crs = CRSToReprojectTo)

We want the CRS of our hillshade to match the DTM_HARV raster. We can thus assign the CRS of our DTM_HARV to our hillshade within the projectRaster() function as follows: crs=crs(DTM_HARV)

# reproject to UTM
DTM_hill_UTMZ18N_HARV <- projectRaster(DTM_hill_HARV, 
                                       crs = crs(DTM_HARV))

# compare attributes of DTM_hill_UTMZ18N to DTM_hill
crs(DTM_hill_UTMZ18N_HARV)
Coordinate Reference System:
Deprecated Proj.4 representation:
 +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs 
WKT2 2019 representation:
PROJCRS["unknown",
    BASEGEOGCRS["unknown",
        DATUM["World Geodetic System 1984",
            ELLIPSOID["WGS 84",6378137,298.257223563,
                LENGTHUNIT["metre",1]],
            ID["EPSG",6326]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8901]]],
    CONVERSION["UTM zone 18N",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",-75,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",0.9996,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",500000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",0,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]],
        ID["EPSG",16018]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]]] 
# compare attributes of DTM_hill_UTMZ18N to DTM_hill
crs(DTM_hill_HARV)
Coordinate Reference System:
Deprecated Proj.4 representation: +proj=longlat +datum=WGS84 +no_defs 
WKT2 2019 representation:
GEOGCRS["unknown",
    DATUM["World Geodetic System 1984",
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]],
        ID["EPSG",6326]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433],
        ID["EPSG",8901]],
    CS[ellipsoidal,2],
        AXIS["longitude",east,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433,
                ID["EPSG",9122]]],
        AXIS["latitude",north,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433,
                ID["EPSG",9122]]]] 
# compare attributes of DTM_hill_UTMZ18N to DTM_hill
extent(DTM_hill_UTMZ18N_HARV)
class      : Extent 
xmin       : 731397.3 
xmax       : 733205.3 
ymin       : 4712403 
ymax       : 4713907 
extent(DTM_hill_HARV)
class      : Extent 
xmin       : -72.18192 
xmax       : -72.16061 
ymin       : 42.52941 
ymax       : 42.54234 

Notice in the output above that the crs() of DTM_hill_UTMZ18N_HARV is now UTM. Also, the extent values of DTM_hillUTMZ18N_HARV are different from DTM_hill_HARV.

Challenge: Extent Change with CRS Change

  1. Why do you think the two extents (of DTM_hillUTMZ18N_HARV and DTM_hill_HARV) differ? While DTM_hill_UTMZ18N_HARV is in UTM (i.e. in terms of meters), DTM_hill_HARV is in terms of latitude and longitude. In the conversion from one projection to another, the units change, so the extents change too.

02-C Deal with Raster Resolution

Let’s next have a look at the resolution of our reprojected hillshade.

# compare resolution
res(DTM_hill_UTMZ18N_HARV)
[1] 1.000 0.998

The output resolution of DTM_hill_UTMZ18N_HARV is 1 x 0.998. Yet, we know that the resolution for the data should be 1m x 1m. We can tell R to force our newly reprojected raster to be 1m x 1m resolution by adding a line of code (res=).

# adjust the resolution 
DTM_hill_UTMZ18N_HARV <- projectRaster(DTM_hill_HARV, 
                                  crs = crs(DTM_HARV),
                                  res = 1)

# view resolution
res(DTM_hill_UTMZ18N_HARV)
[1] 1 1

Let’s plot our newly reprojected raster.

# plot newly reprojected hillshade
plot(DTM_hill_UTMZ18N_HARV,
    col = grey(1:100/100),
    legend = F,
    main = "DTM with Hillshade\n NEON Harvard Forest Field Site")

# overlay the DTM on top of the hillshade
plot(DTM_HARV,
     col = terrain.colors(100),
     alpha = 0.4,
     add = T,
     legend = F)

We have now successfully draped the Digital Terrain Model on top of our hillshade to produce a nice looking, textured map!

Challenge: Reproject a Digital Surface Model Create a map of the San Joaquin Experimental Range field site

Use the NEON-DS-Airborne-Remote-Sensing/SJER/DSM/SJER_dsmCrop.tif and NEON-DS-Airborne-Remote-Sensing/SJER/DSM/SJER_DSMhill_WGS84.tif files.

We’ll load in the two rasterLayers from the files listed above:

DSM_SJER <- raster("NEON-DS-Airborne-Remote-Sensing/SJER/DSM/SJER_dsmCrop.tif")
DSM_hill_UTMZ18N_SJER <- raster("NEON-DS-Airborne-Remote-Sensing/SJER/DSM/SJER_DSMhill_WGS84.tif")
  1. Use the crs() function to look at their coordinate reference systems (focus on the proj string at the top, not the long warning message):
crs(DSM_SJER)
Coordinate Reference System:
Deprecated Proj.4 representation:
 +proj=utm +zone=11 +datum=WGS84 +units=m +no_defs 
WKT2 2019 representation:
PROJCRS["unknown",
    BASEGEOGCRS["unknown",
        DATUM["World Geodetic System 1984",
            ELLIPSOID["WGS 84",6378137,298.257223563,
                LENGTHUNIT["metre",1]],
            ID["EPSG",6326]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8901]]],
    CONVERSION["UTM zone 11N",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",-117,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",0.9996,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",500000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",0,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]],
        ID["EPSG",16011]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]]] 
crs(DSM_hill_UTMZ18N_SJER)
Coordinate Reference System:
Deprecated Proj.4 representation: +proj=longlat +datum=WGS84 +no_defs 
WKT2 2019 representation:
GEOGCRS["unknown",
    DATUM["World Geodetic System 1984",
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]],
        ID["EPSG",6326]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433],
        ID["EPSG",8901]],
    CS[ellipsoidal,2],
        AXIS["longitude",east,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433,
                ID["EPSG",9122]]],
        AXIS["latitude",north,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433,
                ID["EPSG",9122]]]] 
  1. Reproject one of them so they have the same CRS:
DSM_hill_SJER_UTM <- projectRaster(DSM_hill_UTMZ18N_SJER,
                                   crs = crs(DSM_SJER))
  1. Check your work using the crs() function:
crs(DSM_hill_SJER_UTM)
Coordinate Reference System:
Deprecated Proj.4 representation:
 +proj=utm +zone=11 +datum=WGS84 +units=m +no_defs 
WKT2 2019 representation:
PROJCRS["unknown",
    BASEGEOGCRS["unknown",
        DATUM["World Geodetic System 1984",
            ELLIPSOID["WGS 84",6378137,298.257223563,
                LENGTHUNIT["metre",1]],
            ID["EPSG",6326]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8901]]],
    CONVERSION["UTM zone 11N",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",-117,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",0.9996,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",500000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",0,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]],
        ID["EPSG",16011]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]]] 

Great work! You have learned a lot about working with raster data and plotting using base R.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBGaWxlIG1ldGFkYXRhDQoNClRlbXBsYXRlIGF1dGhvcjogTW9yZ2FuIENhcnItTWFya2VsbCAgDQpUZW1wbGF0ZSBsYXN0IG1vZGlmaWVkIG9uOiBGZWIgMjgsIDIwMjMgIA0KTm90ZWJvb2sgbW9kaWZpZWQgYnk6IEthdGllIFdhbmcgIA0KTm90ZWJvb2sgbGFzdCBtb2RpZmllZCBvbjogTWFyIDEsIDIwMjMgIA0KDQoNCiMjIFNvdXJjZQ0KDQpUaGUgY29kZSBhbmQgbWFueSBkZXNjcmlwdGlvbnMgaW4gdGhpcyBub3RlYm9vayBjb21lIGZyb20gYSB0dXRvcmlhbCBwb3N0ZWQgb24gdGhlIChOYXRpb25hbCBFY29sb2dpY2FsIE9ic2VydmF0b3J5IE5ldHdvcmspIE5FT04gd2Vic2l0ZSBhbmQgb3JpZ2luYWxseSBmcm9tIHRoZSBEYXRhIENhcnBlbnRyaWVzLiBZb3UgY2FuIGZpbmQgdGhlIG9yaWdpbmFsIHR1dG9yaWFsIGhlcmU6DQpodHRwczovL3d3dy5uZW9uc2NpZW5jZS5vcmcvcmVzb3VyY2VzL2xlYXJuaW5nLWh1Yi90dXRvcmlhbHMvaW50cm9kdWN0aW9uLXdvcmtpbmctcmFzdGVyLWRhdGEtciN0b2dnbGUtMjYNCg0KKipJbXBvcnRhbnQgbm90ZToqKiBSZW1lbWJlciB0bzoNCg0KMS4gZG93bmxvYWQgdGhlIGRhdGEgemlwIGZpbGUNCjIuIHVuemlwIGl0DQozLiBwbGFjZSB0aGUgdW56aXBwZWQgZm9sZGVyIGluIHRoaXMgaHc1IGRpcmVjdG9yeQ0KDQpPdGhlcndpc2UsIHlvdSB3aWxsIGhhdmUgbm8gZGF0YSB0byB3b3JrIHdpdGghDQoNCg0KIyBHZXR0aW5nIHRvIHVuZGVyc3RhbmQgdGhlIGRhdGENCg0KQmVmb3JlIHlvdSBzdGFydCB0aGUgdHV0b3JpYWwgb24gbG9hZGluZywgcGxvdHRpbmcsIGFuZCBwcm9jZXNzaW5nIHJhc3RlciBkYXRhIGluIFIsIGdvIHRvIHRoaXMgW05FT04gdHV0b3JpYWxdKGh0dHBzOi8vd3d3Lm5lb25zY2llbmNlLm9yZy9yZXNvdXJjZXMvbGVhcm5pbmctaHViL3R1dG9yaWFscy9jaG0tZHNtLWR0bS1ncmlkZGVkLWxpZGFyLWRhdGEpIGFuZCByZWFkIGp1c3QgdG8gdGhlIGVuZCBvZiB0aGUgc2VjdGlvbiBjYWxsZWQgIkdyaWRkZWQsIG9yIFJhc3RlciwgTGlEQVIgRGF0YSBQcm9kdWN0cyINCg0KMS4gV2hhdCBkbyBEVE0sIERTTSwgYW5kIENITSBzdGFuZCBmb3I/ICANCkRUTTogRGlnaXRhbCBUZXJyYWluIE1vZGVsLiAgDQpEU006IERpZ2l0YWwgU3VyZmFjZSBNb2RlbC4gIA0KQ0hNID0gRFNNIC0gRFRNOyBpdCdzIHRoZSBDYW5vcHkgSGVpZ2h0IE1vZGVsLg0KDQoyLiBXaGF0IGFyZSB0aGUgdHdvIG1haW4gZm9ybWF0cyBvZiBMaURBUiBkYXRhIGZpbGVzIGFuZCBob3cgYXJlIHRoZXkgZGlmZmVyZW50PyAgDQpPbmUgaXMgdGhlIC5sYXMgZmlsZSBmb3JtYXQgZm9yIExJREFSIHBvaW50IGNsb3Vkcy4gVGhlc2UgYXJlIGNvbGxlY3Rpb25zIG9mIHBvaW50cyB3aXRoICh4LCB5LCB6KSB2YWx1ZXMgYW5kIG90aGVyIGF0dHJpYnV0ZXMsIGFuZCBhcmUgbm90IGFsaWduZWQgd2l0aCBhIHBhcnRpY3VsYXIgZ3JpZC4gVGhlIG90aGVyIGlzIHRoZSBncmlkZGVkIG9yIHJhc3RlciBkYXRhIGZvcm1hdCwgd2hpY2ggaXMgYSBncmlkIG9mIGNlbGxzIGFsbCBvZiB0aGUgc2FtZSBzaXplLiBFYWNoIGNlbGwgcmVwcmVzZW50cyBhIGNlcnRhaW4gYW1vdW50IG9mIGFyZWEgb24gdGhlIGdyb3VuZC4NCg0KDQotLS0tDQoNCiMgUmFzdGVyIDAwOiBJbnRybyB0byBSYXN0ZXIgRGF0YSBpbiBSDQoNCk9yaWdpbmFsIEF1dGhvcnM6IExlYWggQS4gV2Fzc2VyLCBNZWdhbiBBLiBKb25lcywgWmFjayBCcnltLCBLcmlzdGluYSBSaWVtZXIsIEphc29uIFdpbGxpYW1zLCBKZWZmIEhvbGxpc3RlciwgTWlrZSBTbW9ydWwNCg0KT3JpZ2luYWwgTGFzdCBVcGRhdGVkOiBBcHIgOCwgMjAyMQ0KDQpJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIHJldmlldyB0aGUgZnVuZGFtZW50YWwgcHJpbmNpcGxlcywgcGFja2FnZXMgYW5kIG1ldGFkYXRhL3Jhc3RlciBhdHRyaWJ1dGVzIHRoYXQgYXJlIG5lZWRlZCB0byB3b3JrIHdpdGggcmFzdGVyIGRhdGEgaW4gUi4gV2UgZGlzY3VzcyB0aGUgdGhyZWUgY29yZSBtZXRhZGF0YSBlbGVtZW50cyB0aGF0IHdlIG5lZWQgdG8gdW5kZXJzdGFuZCB0byB3b3JrIHdpdGggcmFzdGVycyBpbiBSOiBDUlMsIGV4dGVudCBhbmQgcmVzb2x1dGlvbi4gSXQgYWxzbyBleHBsb3JlcyBtaXNzaW5nIGFuZCBiYWQgZGF0YSB2YWx1ZXMgYXMgc3RvcmVkIGluIGEgcmFzdGVyIGFuZCBob3cgUiBoYW5kbGVzIHRoZXNlIGVsZW1lbnRzLiBGaW5hbGx5LCBpdCBpbnRyb2R1Y2VzIHRoZSBHZW9UaWZmIGZpbGUgZm9ybWF0Lg0KDQoNCiMjIDAwLUEgTG9hZGluZyBsaWJyYXJpZXMNCg0KYGBge3J9DQpvcHRpb25zKCJyZ2RhbF9zaG93X2V4cG9ydFRvUHJvajRfd2FybmluZ3MiPSJub25lIikgIyB0byBzdXBwcmVzcyB3YXJuaW5nIG1lc3NhZ2VzDQpsaWJyYXJ5KHJhc3RlcikNCmxpYnJhcnkocmdkYWwpDQpsaWJyYXJ5KHJhc3RlclZpcykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCg0KIyMgMDAtQiBMb2FkaW5nIHRoZSBkYXRhDQoNCmBgYHtyfQ0KIyBMb2FkIHJhc3RlciBpbnRvIFINCkRTTV9IQVJWIDwtIHJhc3RlcigiTkVPTi1EUy1BaXJib3JuZS1SZW1vdGUtU2Vuc2luZy9IQVJWL0RTTS9IQVJWX2RzbUNyb3AudGlmIikNCg0KIyBWaWV3IHJhc3RlciBzdHJ1Y3R1cmUNCkRTTV9IQVJWIA0KYGBgDQoNCg0KIyMgMDAtQyBQbG90dGluZyB3aXRoIGJhc2UgUiBwbG90IGZ1bmN0aW9uDQoNCmBgYHtyfQ0KIyBwbG90IHJhc3Rlcg0KIyBub3RlIFxuIGluIHRoZSB0aXRsZSBmb3JjZXMgYSBsaW5lIGJyZWFrIGluIHRoZSB0aXRsZQ0KcGxvdChEU01fSEFSViwgDQogICAgIG1haW49Ik5FT04gRGlnaXRhbCBTdXJmYWNlIE1vZGVsXG5IYXJ2YXJkIEZvcmVzdCIpDQpgYGANCg0KDQojIyAwMC1EIFR5cGVzIG9mIERhdGEgU3RvcmVkIGluIFJhc3RlciBGb3JtYXQNCg0KUmFzdGVyIGRhdGEgY2FuIGJlIGNvbnRpbnVvdXMgb3IgY2F0ZWdvcmljYWwuIENvbnRpbnVvdXMgcmFzdGVycyBjYW4gaGF2ZSBhIHJhbmdlIG9mIHF1YW50aXRhdGl2ZSB2YWx1ZXMuIFNvbWUgZXhhbXBsZXMgb2YgY29udGludW91cyByYXN0ZXJzIGluY2x1ZGU6DQoNCiogUHJlY2lwaXRhdGlvbiBtYXBzLg0KKiBNYXBzIG9mIHRyZWUgaGVpZ2h0IGRlcml2ZWQgZnJvbSBMaURBUiBkYXRhLg0KKiBFbGV2YXRpb24gdmFsdWVzIGZvciBhIHJlZ2lvbi4NCg0KVGhlIHJhc3RlciB3ZSBsb2FkZWQgYW5kIHBsb3R0ZWQgZWFybGllciB3YXMgYSBkaWdpdGFsIHN1cmZhY2UgbW9kZWwsIG9yIGEgbWFwIG9mIHRoZSBlbGV2YXRpb24gZm9yIEhhcnZhcmQgRm9yZXN0IGRlcml2ZWQgZnJvbSB0aGUgTkVPTiBBT1AgTGlEQVIgc2Vuc29yLiBFbGV2YXRpb24gaXMgcmVwcmVzZW50ZWQgYXMgYSBjb250aW51b3VzIG51bWVyaWMgdmFyaWFibGUgaW4gdGhpcyBtYXAuIFRoZSBsZWdlbmQgc2hvd3MgdGhlIGNvbnRpbnVvdXMgcmFuZ2Ugb2YgdmFsdWVzIGluIHRoZSBkYXRhIGZyb20gYXJvdW5kIDMwMCB0byA0MjAgbWV0ZXJzLg0KDQpTb21lIHJhc3RlcnMgY29udGFpbiBjYXRlZ29yaWNhbCBkYXRhIHdoZXJlIGVhY2ggcGl4ZWwgcmVwcmVzZW50cyBhIGRpc2NyZXRlIGNsYXNzIHN1Y2ggYXMgYSBsYW5kY292ZXIgdHlwZSAoZS5nLiwgImZvcmVzdCIgb3IgImdyYXNzbGFuZCIpIHJhdGhlciB0aGFuIGEgY29udGludW91cyB2YWx1ZSBzdWNoIGFzIGVsZXZhdGlvbiBvciB0ZW1wZXJhdHVyZS4gU29tZSBleGFtcGxlcyBvZiBjbGFzc2lmaWVkIG1hcHMgaW5jbHVkZToNCg0KKiBMYW5kY292ZXIvbGFuZC11c2UgbWFwcy4NCiogVHJlZSBoZWlnaHQgbWFwcyBjbGFzc2lmaWVkIGFzIHNob3J0LCBtZWRpdW0sIHRhbGwgdHJlZXMuDQoqIEVsZXZhdGlvbiBtYXBzIGNsYXNzaWZpZWQgYXMgbG93LCBtZWRpdW0gYW5kIGhpZ2ggZWxldmF0aW9uLg0KDQoqTm90ZToqIFNraXBwZWQgc2VjdGlvbiBvbiBjcmVhdGluZyBjYXRlZ29yaWNhbCBwbG90IGJlY2F1c2UgaXQgaXMgY292ZXJlZCBpbiBtb3JlIGRldGFpbCBiZWxvdw0KDQoNCiMjIDAwLUUgV2hhdCBpcyBhIEdlb1RJRkY/Pw0KDQpSYXN0ZXIgZGF0YSBjYW4gY29tZSBpbiBtYW55IGRpZmZlcmVudCBmb3JtYXRzLiBJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIHVzZSB0aGUgZ2VvdGlmZiBmb3JtYXQgd2hpY2ggaGFzIHRoZSBleHRlbnNpb24gLnRpZi4gQSAudGlmIGZpbGUgc3RvcmVzIG1ldGFkYXRhIG9yIGF0dHJpYnV0ZXMgYWJvdXQgdGhlIGZpbGUgYXMgZW1iZWRkZWQgdGlmIHRhZ3MuIEZvciBpbnN0YW5jZSwgeW91ciBjYW1lcmEgbWlnaHQgc3RvcmUgYSB0YWcgdGhhdCBkZXNjcmliZXMgdGhlIG1ha2UgYW5kIG1vZGVsIG9mIHRoZSBjYW1lcmEgb3IgdGhlIGRhdGUgdGhlIHBob3RvIHdhcyB0YWtlbiB3aGVuIGl0IHNhdmVzIGEgLnRpZi4gQSBHZW9USUZGIGlzIGEgc3RhbmRhcmQgLnRpZiBpbWFnZSBmb3JtYXQgd2l0aCBhZGRpdGlvbmFsIHNwYXRpYWwgKGdlb3JlZmVyZW5jaW5nKSBpbmZvcm1hdGlvbiBlbWJlZGRlZCBpbiB0aGUgZmlsZSBhcyB0YWdzLiBUaGVzZSB0YWdzIGNhbiBpbmNsdWRlIHRoZSBmb2xsb3dpbmcgcmFzdGVyIG1ldGFkYXRhOg0KDQoqIEEgQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtIChDUlMpDQoqIFNwYXRpYWwgRXh0ZW50IChleHRlbnQpDQoqIFZhbHVlcyB0aGF0IHJlcHJlc2VudCBtaXNzaW5nIGRhdGEgKE5vRGF0YVZhbHVlKQ0KKiBUaGUgcmVzb2x1dGlvbiBvZiB0aGUgZGF0YQ0KDQpJbiB0aGlzIHR1dG9yaWFsIHdlIHdpbGwgZGlzY3VzcyBhbGwgb2YgdGhlc2UgbWV0YWRhdGEgdGFncy4NCg0KTW9yZSBhYm91dCB0aGUgLnRpZiBmb3JtYXQ6DQoNCiogW0dlb1RJRkYgb24gV2lraXBlZGlhXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9USUZGKQ0KKiBbT1NHRU8gVElGRiBkb2N1bWVudGF0aW9uXShodHRwczovL3RyYWMub3NnZW8ub3JnL2dlb3RpZmYvKQ0KDQoqTm90ZToqIEkgYW0gc2tpcHBpbmcgdGhlIENSUyBwYXJ0IG9mIHRoZSB0dXRvcmlhbCBiZWNhdXNlIHdlIGNvdmVyZWQgdGhhdCBsYXN0IHdlZWssIGJ1dCBpZiB5b3Ugd2FudCBhIHJlZnJlc2hlciB5b3UgY2FuIGxvb2sgYXQgdGhhdCBzZWN0aW9uIG9mIHRoZSBvbmxpbmUgdHV0b3JpYWwgW2hlcmVdKGh0dHBzOi8vd3d3Lm5lb25zY2llbmNlLm9yZy9yZXNvdXJjZXMvbGVhcm5pbmctaHViL3R1dG9yaWFscy9pbnRyb2R1Y3Rpb24td29ya2luZy1yYXN0ZXItZGF0YS1yI3RvZ2dsZS0wKQ0KDQoNCiMjIDAwLUYgRXh0ZW50DQoNClRoZSBzcGF0aWFsIGV4dGVudCBpcyB0aGUgZ2VvZ3JhcGhpYyBhcmVhIHRoYXQgdGhlIHJhc3RlciBkYXRhIGNvdmVycy4NCg0KVGhlIHNwYXRpYWwgZXh0ZW50IG9mIGFuIFIgc3BhdGlhbCBvYmplY3QgcmVwcmVzZW50cyB0aGUgZ2VvZ3JhcGhpYyAiZWRnZSIgb3IgbG9jYXRpb24gdGhhdCBpcyB0aGUgZnVydGhlc3Qgbm9ydGgsIHNvdXRoLCBlYXN0IGFuZCB3ZXN0LiBJbiBvdGhlciB3b3JkcywgZXh0ZW50IHJlcHJlc2VudHMgdGhlIG92ZXJhbGwgZ2VvZ3JhcGhpYyBjb3ZlcmFnZSBvZiB0aGUgc3BhdGlhbCBvYmplY3QuDQoNCg0KIyMgMDAtRyBSZXNvbHV0aW9uDQoNCkEgcmFzdGVyIGhhcyBob3Jpem9udGFsICh4IGFuZCB5KSByZXNvbHV0aW9uLiBUaGlzIHJlc29sdXRpb24gcmVwcmVzZW50cyB0aGUgYXJlYSBvbiB0aGUgZ3JvdW5kIHRoYXQgZWFjaCBwaXhlbCBjb3ZlcnMuIFRoZSB1bml0cyBmb3Igb3VyIGRhdGEgYXJlIGluIG1ldGVycy4gR2l2ZW4gb3VyIGRhdGEgcmVzb2x1dGlvbiBpcyAxIHggMSwgdGhpcyBtZWFucyB0aGF0IGVhY2ggcGl4ZWwgcmVwcmVzZW50cyBhIDEgeCAxIG1ldGVyIGFyZWEgb24gdGhlIGdyb3VuZC4NCg0KVGhlIGJlc3Qgd2F5IHRvIHZpZXcgcmVzb2x1dGlvbiB1bml0cyBpcyB0byBsb29rIGF0IHRoZSBjb29yZGluYXRlIHJlZmVyZW5jZSBzeXN0ZW0gc3RyaW5nIGNycygpLiBOb3RpY2Ugb3VyIGRhdGEgY29udGFpbnM6ICt1bml0cz1tLg0KDQoqTm90ZToqIFlvdSdsbCBzZWUgYSB3YXJuaW5nIGFib3V0IHRoZSBQcm9qLjQgcmVwcmVzZW50YXRpb24gYmVpbmcgZGVwcmVjYXRlZC4gWW91IGNhbiBpZ25vcmUgdGhhdC4NCg0KYGBge3J9DQpjcnMoRFNNX0hBUlYpDQpgYGANCg0KKk5vdGU6KiBJIGFtIHNraXBwaW5nIHRoZSByYXN0ZXIgbWluIGFuZCBtYXggdmFsdWVzIHNlY3Rpb24NCg0KDQojIyAwMC1IIE5vRGF0YSBWYWx1ZXMgaW4gUmFzdGVycw0KDQpSYXN0ZXIgZGF0YSBvZnRlbiBoYXMgYSBOb0RhdGFWYWx1ZSBhc3NvY2lhdGVkIHdpdGggaXQuIFRoaXMgaXMgYSB2YWx1ZSBhc3NpZ25lZCB0byBwaXhlbHMgd2hlcmUgZGF0YSBhcmUgbWlzc2luZyBvciBubyBkYXRhIHdlcmUgY29sbGVjdGVkLg0KDQpCeSBkZWZhdWx0IHRoZSBzaGFwZSBvZiBhIHJhc3RlciBpcyBhbHdheXMgc3F1YXJlIG9yIHJlY3Rhbmd1bGFyLiBTbyBpZiB3ZSBoYXZlIGEgZGF0YXNldCB0aGF0IGhhcyBhIHNoYXBlIHRoYXQgaXNuJ3Qgc3F1YXJlIG9yIHJlY3Rhbmd1bGFyLCBzb21lIHBpeGVscyBhdCB0aGUgZWRnZSBvZiB0aGUgcmFzdGVyIHdpbGwgaGF2ZSBOb0RhdGFWYWx1ZXMuIFRoaXMgb2Z0ZW4gaGFwcGVucyB3aGVuIHRoZSBkYXRhIHdlcmUgY29sbGVjdGVkIGJ5IGFuIGFpcnBsYW5lIHdoaWNoIG9ubHkgZmxldyBvdmVyIHNvbWUgcGFydCBvZiBhIGRlZmluZWQgcmVnaW9uLg0KDQpJbiB0aGUgaW1hZ2UgYmVsb3csIHRoZSBwaXhlbHMgdGhhdCBhcmUgYmxhY2sgaGF2ZSBOb0RhdGFWYWx1ZXMuIFRoZSBjYW1lcmEgZGlkIG5vdCBjb2xsZWN0IGRhdGEgaW4gdGhlc2UgYXJlYXMuDQoNCmBgYHtyfQ0KIyBzdGFjaygpIGlzIGEgcmFzdGVyIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIHJhc3RlclN0YWNrIG9iamVjdA0KUkdCX3N0YWNrIDwtIHN0YWNrKCJORU9OLURTLUFpcmJvcm5lLVJlbW90ZS1TZW5zaW5nL0hBUlYvUkdCX0ltYWdlcnkvSEFSVl9SR0JfT3J0aG8udGlmIikNCg0KIyBDcmVhdGUgYW4gUkdCIGltYWdlIGZyb20gdGhlIHJhc3RlciBzdGFjaw0KcGFyKGNvbC5heGlzID0gIndoaXRlIiwgY29sLmxhYiA9ICJ3aGl0ZSIsIHRjayA9IDApDQojIE5vdGU6IGNvbC5heGlzIGFuZCBjb2wubGFiIHNldCB0aGUgY29sb3JzIGZvciB0aGUgYXhpcyBhbm5vdGF0aW9uIGFuZCB4L3kgbGFiZWxzDQogICAgICAjIHRjayBpcyB0aGUgbGVuZ3RoIG9mIHRpY2sgbWFya3Mgb24gdGhlIGF4ZXMNCg0KIyBUaGUgY29kZSBiZWxvdyBwbG90cyBhbiBSR0IgY29sb3IgaW1hZ2UgdXNpbmcgbGF5ZXJzIGluIGEgcmFzdGVyU3RhY2sgb2JqZWN0DQojIFdlIGhhdmUgdG8gdGVsbCB0aGUgZnVuY3Rpb24gd2hpY2ggbGF5ZXJzIGluIHRoZSBzdGFjayBjb250YWluIHJlZCBncmVlbiBhbmQgYmx1ZQ0KcGxvdFJHQihSR0Jfc3RhY2ssIA0KICAgICAgICByID0gMSwgDQogICAgICAgIGcgPSAyLCANCiAgICAgICAgYiA9IDMsIA0KICAgICAgICBheGVzID0gVFJVRSwgDQogICAgICAgIG1haW4gPSAiUmFzdGVyIFdpdGggTm9EYXRhIFZhbHVlc1xuUmVuZGVyZWQgaW4gQmxhY2siKQ0KYGBgDQoNCipOb3RlOiogU2tpcHBlZCBzZWN0aW9uIG9uIHNldHRpbmcgTm9EYXRhIHZhbHVlcyB0byBOQQ0KDQoNCiMjIDAwLUkgTm9EYXRhIFZhbHVlIFN0YW5kYXJkDQoNClRoZSBhc3NpZ25lZCBOb0RhdGFWYWx1ZSB2YXJpZXMgYWNyb3NzIGRpc2NpcGxpbmVzOyAtOTk5OSBpcyBhIGNvbW1vbiB2YWx1ZSB1c2VkIGluIGJvdGggdGhlIHJlbW90ZSBzZW5zaW5nIGZpZWxkIGFuZCB0aGUgYXRtb3NwaGVyaWMgZmllbGRzLiBJdCBpcyBhbHNvIHRoZSBzdGFuZGFyZCB1c2VkIGJ5IHRoZSBOYXRpb25hbCBFY29sb2dpY2FsIE9ic2VydmF0b3J5IE5ldHdvcmsgKE5FT04pLg0KDQpJZiB3ZSBhcmUgbHVja3ksIG91ciBHZW9USUZGIGZpbGUgaGFzIGEgdGFnIHRoYXQgdGVsbHMgdXMgd2hhdCBpcyB0aGUgTm9EYXRhVmFsdWUuIElmIHdlIGFyZSBsZXNzIGx1Y2t5LCB3ZSBjYW4gZmluZCB0aGF0IGluZm9ybWF0aW9uIGluIHRoZSByYXN0ZXIncyBtZXRhZGF0YS4gSWYgYSBOb0RhdGFWYWx1ZSB3YXMgc3RvcmVkIGluIHRoZSBHZW9USUZGIHRhZywgd2hlbiBSIG9wZW5zIHVwIHRoZSByYXN0ZXIsIGl0IHdpbGwgYXNzaWduIGVhY2ggaW5zdGFuY2Ugb2YgdGhlIHZhbHVlIHRvIE5BLiBWYWx1ZXMgb2YgTkEgd2lsbCBiZSBpZ25vcmVkIGJ5IFIgYXMgZGVtb25zdHJhdGVkIGFib3ZlLg0KDQoNCiMjIDAwLUogQmFkIERhdGEgVmFsdWVzIGluIFJhc3RlcnMNCg0KQmFkIGRhdGEgdmFsdWVzIGFyZSBkaWZmZXJlbnQgZnJvbSBOb0RhdGFWYWx1ZXMuIEJhZCBkYXRhIHZhbHVlcyBhcmUgdmFsdWVzIHRoYXQgZmFsbCBvdXRzaWRlIG9mIHRoZSBhcHBsaWNhYmxlIHJhbmdlIG9mIGEgZGF0YXNldC4NCg0KRXhhbXBsZXMgb2YgQmFkIERhdGEgVmFsdWVzOg0KDQoqIFRoZSBub3JtYWxpemVkIGRpZmZlcmVuY2UgdmVnZXRhdGlvbiBpbmRleCAoTkRWSSksIHdoaWNoIGlzIGEgbWVhc3VyZSBvZiBncmVlbm5lc3MsIGhhcyBhIHZhbGlkIHJhbmdlIG9mIC0xIHRvIDEuIEFueSB2YWx1ZSBvdXRzaWRlIG9mIHRoYXQgcmFuZ2Ugd291bGQgYmUgY29uc2lkZXJlZCBhICJiYWQiIG9yIG1pc2NhbGN1bGF0ZWQgdmFsdWUuDQoqIFJlZmxlY3RhbmNlIGRhdGEgaW4gYW4gaW1hZ2Ugd2lsbCBvZnRlbiByYW5nZSBmcm9tIDAtMSBvciAwLTEwLDAwMCBkZXBlbmRpbmcgdXBvbiBob3cgdGhlIGRhdGEgYXJlIHNjYWxlZC4gVGh1cyBhIHZhbHVlIGdyZWF0ZXIgdGhhbiAxIG9yIGdyZWF0ZXIgdGhhbiAxMCwwMDAgaXMgbGlrZWx5IGNhdXNlZCBieSBhbiBlcnJvciBpbiBlaXRoZXIgZGF0YSBjb2xsZWN0aW9uIG9yIHByb2Nlc3NpbmcuDQoNCg0KIyMjIEZpbmQgQmFkIERhdGEgVmFsdWVzDQoNClNvbWV0aW1lcyBhIHJhc3RlcidzIG1ldGFkYXRhIHdpbGwgdGVsbCB1cyB0aGUgcmFuZ2Ugb2YgZXhwZWN0ZWQgdmFsdWVzIGZvciBhIHJhc3Rlci4gVmFsdWVzIG91dHNpZGUgb2YgdGhpcyByYW5nZSBhcmUgc3VzcGVjdCBhbmQgd2UgbmVlZCB0byBjb25zaWRlciB0aGFuIHdoZW4gd2UgYW5hbHl6ZSB0aGUgZGF0YS4gU29tZXRpbWVzLCB3ZSBuZWVkIHRvIHVzZSBzb21lIGNvbW1vbiBzZW5zZSBhbmQgc2NpZW50aWZpYyBpbnNpZ2h0IGFzIHdlIGV4YW1pbmUgdGhlIGRhdGEgLSBqdXN0IGFzIHdlIHdvdWxkIGZvciBmaWVsZCBkYXRhIHRvIGlkZW50aWZ5IHF1ZXN0aW9uYWJsZSB2YWx1ZXMuDQoNCg0KIyMjIENyZWF0ZSBBIEhpc3RvZ3JhbSBvZiBSYXN0ZXIgVmFsdWVzDQoNCldlIGNhbiBleHBsb3JlIHRoZSBkaXN0cmlidXRpb24gb2YgdmFsdWVzIGNvbnRhaW5lZCB3aXRoaW4gb3VyIHJhc3RlciB1c2luZyB0aGUgaGlzdCgpIGZ1bmN0aW9uIHdoaWNoIHByb2R1Y2VzIGEgaGlzdG9ncmFtLiBIaXN0b2dyYW1zIGFyZSBvZnRlbiB1c2VmdWwgaW4gaWRlbnRpZnlpbmcgb3V0bGllcnMgYW5kIGJhZCBkYXRhIHZhbHVlcyBpbiBvdXIgcmFzdGVyIGRhdGEuDQoNCmBgYHtyfQ0KIyB2aWV3IGhpc3RvZ3JhbSBvZiBkYXRhDQpoaXN0KERTTV9IQVJWLA0KICAgICBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBEaWdpdGFsIFN1cmZhY2UgTW9kZWwgVmFsdWVzXG4gSGlzdG9ncmFtIERlZmF1bHQ6IDEwMCwwMDAgcGl4ZWxzXG4gTkVPTiBIYXJ2YXJkIEZvcmVzdCIsDQogICAgIHhsYWIgPSAiRFNNIEVsZXZhdGlvbiBWYWx1ZSAobSkiLA0KICAgICB5bGFiID0gIkZyZXF1ZW5jeSIsDQogICAgIGNvbCA9ICJ3aGVhdCIpDQpgYGANCg0KTm90aWNlIHRoYXQgYSB3YXJuaW5nIGlzIHNob3duIHdoZW4gUiBjcmVhdGVzIHRoZSBoaXN0b2dyYW0uDQoNClRoaXMgd2FybmluZyBpcyBjYXVzZWQgYnkgdGhlIGRlZmF1bHQgbWF4aW11bSBwaXhlbHMgdmFsdWUgb2YgMTAwLDAwMCBhc3NvY2lhdGVkIHdpdGggdGhlIGhpc3QgZnVuY3Rpb24uIFRoaXMgbWF4aW11bSB2YWx1ZSBpcyB0byBlbnN1cmUgcHJvY2Vzc2luZyBlZmZpY2llbmN5IGFzIG91ciBkYXRhIGJlY29tZSBsYXJnZXIhDQoNCiogTW9yZSBvbiBoaXN0b2dyYW1zIGluIFtSIGZyb20gUi1ibG9nZ2Vyc10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vMjAxMi8xMi9iYXNpY3Mtb2YtaGlzdG9ncmFtcy8pDQoNCldlIGNhbiBkZWZpbmUgdGhlIG1heCBwaXhlbHMgdG8gZW5zdXJlIHRoYXQgYWxsIHBpeGVsIHZhbHVlcyBhcmUgaW5jbHVkZWQgaW4gdGhlIGhpc3RvZ3JhbS4gVVNFIFRISVMgV0lUSCBDQVVUSU9OIGFzIGZvcmNpbmcgUiB0byBwbG90IGFsbCBwaXhlbCB2YWx1ZXMgaW4gYSBoaXN0b2dyYW0gY2FuIGJlIHByb2JsZW1hdGljIHdoZW4gZGVhbGluZyB3aXRoIHZlcnkgbGFyZ2UgZGF0YXNldHMuDQoNCmBgYHtyfQ0KIyBWaWV3IHRoZSB0b3RhbCBudW1iZXIgb2YgcGl4ZWxzIChjZWxscykgaW4gaXMgb3VyIHJhc3RlciANCnRvdGFsQ2VsbHMgPC0gbmNlbGwoRFNNX0hBUlYpDQpgYGANCg0KTm93IHdlIGNhbiBhZGQgdGhhdCBhcyBhIHBhcmFtZXRlciwgbWF4cGl4ZWxzOg0KDQpgYGB7cn0NCiMgY3JlYXRlIGhpc3RvZ3JhbSB0aGF0IGluY2x1ZGVzIHdpdGggYWxsIHBpeGVsIHZhbHVlcyBpbiB0aGUgcmFzdGVyDQpoaXN0KERTTV9IQVJWLCANCiAgICAgbWF4cGl4ZWxzID0gdG90YWxDZWxscywNCiAgICAgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgRFNNIFZhbHVlc1xuIEFsbCBQaXhlbCBWYWx1ZXMgSW5jbHVkZWRcbiBORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUiLA0KICAgICB4bGFiID0gIkRTTSBFbGV2YXRpb24gVmFsdWUgKG0pIiwNCiAgICAgeWxhYiA9ICJGcmVxdWVuY3kiLA0KICAgICBjb2wgPSAid2hlYXQ0IikNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIHNoYXBlIG9mIGJvdGggaGlzdG9ncmFtcyBsb29rcyBzaW1pbGFyIHRvIHRoZSBwcmV2aW91cyBvbmUgdGhhdCB3YXMgY3JlYXRlZCB1c2luZyBhIHJlcHJlc2VudGF0aXZlIDEwLDAwMCBwaXhlbCBzdWJzZXQgb2Ygb3VyIHJhc3RlciBkYXRhLiBUaGUgZGlzdHJpYnV0aW9uIG9mIGVsZXZhdGlvbiB2YWx1ZXMgZm9yIG91ciBEaWdpdGFsIFN1cmZhY2UgTW9kZWwgKERTTSkgbG9va3MgcmVhc29uYWJsZS4gSXQgaXMgbGlrZWx5IHRoZXJlIGFyZSBubyBiYWQgZGF0YSB2YWx1ZXMgaW4gdGhpcyBwYXJ0aWN1bGFyIHJhc3Rlci4NCg0KDQojIyAwMC1LIFJhc3RlciBCYW5kcw0KDQpUaGUgRGlnaXRhbCBTdXJmYWNlIE1vZGVsIG9iamVjdCAoRFNNX0hBUlYpIHRoYXQgd2UndmUgYmVlbiB3b3JraW5nIHdpdGggaXMgYSBzaW5nbGUgYmFuZCByYXN0ZXIuIFRoaXMgbWVhbnMgdGhhdCB0aGVyZSBpcyBvbmx5IG9uZSBkYXRhc2V0IHN0b3JlZCBpbiB0aGUgcmFzdGVyOiBzdXJmYWNlIGVsZXZhdGlvbiBpbiBtZXRlcnMgZm9yIG9uZSB0aW1lIHBlcmlvZC4NCg0KQSByYXN0ZXIgZGF0YXNldCBjYW4gY29udGFpbiBvbmUgb3IgbW9yZSBiYW5kcy4gV2UgY2FuIHVzZSB0aGUgcmFzdGVyKCkgZnVuY3Rpb24gdG8gaW1wb3J0IG9uZSBzaW5nbGUgYmFuZCBmcm9tIGEgc2luZ2xlIE9SIG11bHRpLWJhbmQgcmFzdGVyLiBXZSBjYW4gdmlldyB0aGUgbnVtYmVyIG9mIGJhbmRzIGluIGEgcmFzdGVyIHVzaW5nIHRoZSBubGF5ZXJzKCkgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KIyB2aWV3IG51bWJlciBvZiBiYW5kcw0KbmxheWVycyhEU01fSEFSVikNCmBgYA0KDQpIb3dldmVyLCByYXN0ZXIgZGF0YSBjYW4gYWxzbyBiZSBtdWx0aS1iYW5kIG1lYW5pbmcgdGhhdCBvbmUgcmFzdGVyIGZpbGUgY29udGFpbnMgZGF0YSBmb3IgbW9yZSB0aGFuIG9uZSB2YXJpYWJsZSBvciB0aW1lIHBlcmlvZCBmb3IgZWFjaCBjZWxsLiBCeSBkZWZhdWx0IHRoZSByYXN0ZXIoKSBmdW5jdGlvbiBvbmx5IGltcG9ydHMgdGhlIGZpcnN0IGJhbmQgaW4gYSByYXN0ZXIgcmVnYXJkbGVzcyBvZiB3aGV0aGVyIGl0IGhhcyBvbmUgb3IgbW9yZSBiYW5kcy4NCg0KDQojIyAwMC1MIFZpZXcgUmFzdGVyIEZpbGUgQXR0cmlidXRlcw0KDQpSZW1lbWJlciB0aGF0IGEgR2VvVElGRiBjb250YWlucyBhIHNldCBvZiBlbWJlZGRlZCB0YWdzIHRoYXQgY29udGFpbiBtZXRhZGF0YSBhYm91dCB0aGUgcmFzdGVyLiBTbyBmYXIsIHdlJ3ZlIGV4cGxvcmVkIHJhc3RlciBtZXRhZGF0YSBhZnRlciBpbXBvcnRpbmcgaXQgaW4gUi4gSG93ZXZlciwgd2UgY2FuIHVzZSB0aGUgR0RBTGluZm8oInBhdGgtdG8tcmFzdGVyLWhlcmUiKSBmdW5jdGlvbiB0byB2aWV3IHJhc3RlciBtZXRhZGF0YSBiZWZvcmUgd2Ugb3BlbiBhIGZpbGUgaW4gUi4NCg0KKk5vdGU6KiBJbiB0aGlzIG5vdGVib29rLCB5b3UnbGwgbmVlZCB0byBjbGljayB0aHJvdWdoIHRoZSB0aHJlZSBkYXRhIGZyYW1lcyB0byBzZWUgYWxsIG9mIHRoZSBpbmZvIGFib3V0IHRoaXMgcmFzdGVyTGF5ZXIuDQoNCmBgYHtyfQ0KIyB2aWV3IGF0dHJpYnV0ZXMgYmVmb3JlIG9wZW5pbmcgZmlsZQ0KR0RBTGluZm8oIk5FT04tRFMtQWlyYm9ybmUtUmVtb3RlLVNlbnNpbmcvSEFSVi9EU00vSEFSVl9kc21Dcm9wLnRpZiIpDQpgYGANCg0KTm90aWNlIGEgZmV3IHRoaW5ncyBpbiB0aGUgb3V0cHV0Og0KDQoqIEEgcHJvamVjdGlvbiBpcyBkZXNjcmliZWQgdXNpbmcgYSBzdHJpbmcgaW4gdGhlIHByb2o0IGZvcm1hdCA6ICtwcm9qPXV0bSArem9uZT0xOCArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMNCiogV2UgY2FuIGlkZW50aWZ5IGEgTm9EYXRhVmFsdWU6IC05OTk5DQoqIFdlIGNhbiB0ZWxsIGhvdyBtYW55IGJhbmRzIHRoZSBmaWxlIGNvbnRhaW5zOiAxDQoqIFdlIGNhbiB2aWV3IHRoZSB4IGFuZCB5IHJlc29sdXRpb24gb2YgdGhlIGRhdGE6IDENCiogV2UgY2FuIHNlZSB0aGUgbWluIGFuZCBtYXggdmFsdWVzIG9mIHRoZSBkYXRhOiBCbWluIGFuZCBCbWF4Lg0KDQpJdCBpcyBpZGVhbCB0byB1c2UgR0RBTGluZm8gdG8gZXhwbG9yZSB5b3VyIGZpbGUgYmVmb3JlIHJlYWRpbmcgaXQgaW50byBSLg0KDQoqTm90ZToqIHJnZGFsIGlzIGJlaW5nIHJlcGxhY2VkIGJ5IGZ1bmN0aW9ucyBpbiBzZiBhbmQgdGVycmEvc3RhcnMgc28gdGhpcyB3b24ndCBiZSB0cnVlIGluIHRoZSBmdXR1cmUuDQoNCg0KIyBDSEFMTEVOR0U6IEVYUExPUkUgUkFTVEVSIE1FVEFEQVRBDQoNCjEuIFdpdGhvdXQgdXNpbmcgdGhlIHJhc3RlciBmdW5jdGlvbiB0byByZWFkIHRoZSBmaWxlIGludG8gUiwgZGV0ZXJtaW5lIHRoZSBmb2xsb3dpbmcgYWJvdXQgdGhlIE5FT04tRFMtQWlyYm9ybmUtUmVtb3RlLVNlbnNpbmcvSEFSVi9EU00vSEFSVl9EU01oaWxsLnRpZiBmaWxlOg0KDQpgYGB7cn0NCkdEQUxpbmZvKCJORU9OLURTLUFpcmJvcm5lLVJlbW90ZS1TZW5zaW5nL0hBUlYvRFNNL0hBUlZfRFNNaGlsbC50aWYiKQ0KYGBgDQoNCjIuIERvZXMgdGhpcyBmaWxlIGhhdmUgdGhlIHNhbWUgQ1JTIGFzIERTTV9IQVJWPyAgDQpZZXM7IHRoZXkgYm90aCBoYXZlIHByb2o9dXRtLg0KDQozLiBXaGF0IGlzIHRoZSBOb0RhdGFWYWx1ZT8gIA0KTm9EYXRhVmFsdWUgaXMgLTk5OTkuDQoNCjQuIFdoYXQgaXMgcmVzb2x1dGlvbiBvZiB0aGUgcmFzdGVyIGRhdGE/ICANClRoZSB4IGFuZCB5IHJlc29sdXRpb24gYXJlIGJvdGggMS4NCg0KNS4gSG93IGxhcmdlIHdvdWxkIGEgMXgxIHBpeGVsIGFyZWEgd291bGQgYmUgb24gdGhlIEVhcnRoJ3Mgc3VyZmFjZT8gIA0KQmVjYXVzZSByZXMueCBhbmQgcmVzLnkgYXJlIDEsIGFuZCB0aGUgdW5pdHMgb2YgdGhlIHByb2plY3Rpb24gc3lzdGVtIGFyZSBtZXRlcnMsIGVhY2ggcGl4ZWwgaXMgYSAxbSB4IDFtIGFyZWEgb24gRWFydGguDQoNCjYuIElzIHRoZSBmaWxlIGEgbXVsdGktIG9yIHNpbmdsZS1iYW5kIHJhc3Rlcj8gIA0KVGhpcyBmaWxlIGhhcyAxIGJhbmQuDQoNCk5vdGljZTogdGhpcyBmaWxlIGlzIGEgaGlsbHNoYWRlLCB3aGljaCB1c2VzIGluZm9ybWF0aW9uIGFib3V0IGVsZXZhdGlvbiB0byBhZGQgc2hhZG93cyB0byBtYWtlIGNoYW5nZXMgaW4gZWxldmF0aW9uIGNsZWFyZXIgdG8gYSBtYXAgdmlld2VyLg0KDQoNCi0tLS0NCg0KIyBSYXN0ZXIgMDE6IFBsb3QgUmFzdGVyIERhdGEgaW4gUg0KDQpPcmlnaW5hbCBBdXRob3JzOiBMZWFoIEEuIFdhc3NlciwgTWVnYW4gQS4gSm9uZXMsIFphY2sgQnJ5bSwgS3Jpc3RpbmEgUmllbWVyLCBKYXNvbiBXaWxsaWFtcywgSmVmZiBIb2xsaXN0ZXIsIE1pa2UgU21vcnVsDQoNCk9yaWdpbmFsIExhc3QgVXBkYXRlZDogQXByIDgsIDIwMjENCg0KVGhpcyB0dXRvcmlhbCByZXZpZXdzIGhvdyB0byBwbG90IGEgcmFzdGVyIGluIFIgdXNpbmcgdGhlIHBsb3QoKSBmdW5jdGlvbi4gSXQgYWxzbyBjb3ZlcnMgaG93IHRvIGxheWVyIGEgcmFzdGVyIG9uIHRvcCBvZiBhIGhpbGxzaGFkZSB0byBwcm9kdWNlIGFuIGVsb3F1ZW50IG1hcC4NCg0KDQojIyAwMS1BIFBsb3QgUmFzdGVyIERhdGEgaW4gUg0KDQpJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIHBsb3QgdGhlIERpZ2l0YWwgU3VyZmFjZSBNb2RlbCAoRFNNKSByYXN0ZXIgZm9yIHRoZSBORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUuIFdlIHdpbGwgdXNlIHRoZSBoaXN0KCkgZnVuY3Rpb24gYXMgYSB0b29sIHRvIGV4cGxvcmUgcmFzdGVyIHZhbHVlcy4gQW5kIHJlbmRlciBjYXRlZ29yaWNhbCBwbG90cywgdXNpbmcgdGhlIGJyZWFrcyBhcmd1bWVudCB0byBnZXQgYmlucyB0aGF0IGFyZSBtZWFuaW5nZnVsIHJlcHJlc2VudGF0aW9ucyBvZiBvdXIgZGF0YS4NCg0KRmlyc3QsIGxldCdzIHBsb3Qgb3VyIERpZ2l0YWwgU3VyZmFjZSBNb2RlbCBvYmplY3QgKERTTV9IQVJWKSB1c2luZyB0aGUgcGxvdCgpIGZ1bmN0aW9uLiBXZSBhZGQgYSB0aXRsZSB1c2luZyB0aGUgYXJndW1lbnQgbWFpbj0idGl0bGUiLg0KDQpgYGB7cn0NCiMgUGxvdCByYXN0ZXIgb2JqZWN0DQpwbG90KERTTV9IQVJWLA0KICAgICBtYWluPSJEaWdpdGFsIFN1cmZhY2UgTW9kZWxcbk5FT04gSGFydmFyZCBGb3Jlc3QgRmllbGQgU2l0ZSIpDQpgYGANCg0KDQojIyAwMS1CIFBsb3R0aW5nIERhdGEgVXNpbmcgQnJlYWtzDQoNCldlIGNhbiB2aWV3IG91ciBkYXRhICJzeW1ib2xpemVkIiBvciBjb2xvcmVkIGFjY29yZGluZyB0byByYW5nZXMgb2YgdmFsdWVzIHJhdGhlciB0aGFuIHVzaW5nIGEgY29udGludW91cyBjb2xvciByYW1wLiBUaGlzIGlzIGNvbXBhcmFibGUgdG8gYSAiY2xhc3NpZmllZCIgbWFwLiBIb3dldmVyLCB0byBhc3NpZ24gYnJlYWtzLCBpdCBpcyB1c2VmdWwgdG8gZmlyc3QgZXhwbG9yZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhIHVzaW5nIGEgaGlzdG9ncmFtLiBUaGUgYnJlYWtzIGFyZ3VtZW50IGluIHRoZSBoaXN0KCkgZnVuY3Rpb24gdGVsbHMgUiB0byB1c2UgZmV3ZXIgb3IgbW9yZSBicmVha3Mgb3IgYmlucy4NCg0KSWYgd2UgbmFtZSB0aGUgaGlzdG9ncmFtLCB3ZSBjYW4gYWxzbyB2aWV3IGNvdW50cyBmb3IgZWFjaCBiaW4gYW5kIGFzc2lnbmVkIGJyZWFrIHZhbHVlcy4NCg0KYGBge3J9DQojIFBsb3QgZGlzdHJpYnV0aW9uIG9mIHJhc3RlciB2YWx1ZXMgDQpEU01oaXN0IDwtIGhpc3QoRFNNX0hBUlYsDQogICAgIGJyZWFrcyA9IDMsDQogICAgIG1haW4gPSAiSGlzdG9ncmFtIERpZ2l0YWwgU3VyZmFjZSBNb2RlbFxuIE5FT04gSGFydmFyZCBGb3Jlc3QgRmllbGQgU2l0ZSIsDQogICAgIGNvbCA9ICJ3aGVhdDMiLCAgIyBjaGFuZ2VzIGJpbiBjb2xvcg0KICAgICB4bGFiID0gIkVsZXZhdGlvbiAobSkiKSAgIyBsYWJlbCB0aGUgeC1heGlzDQpgYGANCg0KV2FybmluZyBtZXNzYWdlIT8gUmVtZW1iZXIsIHRoZSBkZWZhdWx0IGZvciB0aGUgaGlzdG9ncmFtIGlzIHRvIGluY2x1ZGUgb25seSBhIHN1YnNldCBvZiAxMDAsMDAwIHZhbHVlcy4gV2UgY291bGQgZm9yY2UgaXQgdG8gc2hvdyBhbGwgdGhlIHBpeGVsIHZhbHVlcyBvciB3ZSBjYW4gdXNlIHRoZSBoaXN0b2dyYW0gYXMgaXMgYW5kIGZpZ3VyZSB0aGF0IHRoZSBzYW1wbGUgb2YgMTAwLDAwMCB2YWx1ZXMgcmVwcmVzZW50cyBvdXIgZGF0YSB3ZWxsLg0KDQpgYGB7cn0NCiMgV2hlcmUgYXJlIHRoZSBicmVha3MgYW5kIGhvdyBtYW55IHBpeGVscyBpbiBlYWNoIGNhdGVnb3J5Pw0KRFNNaGlzdCRicmVha3MNCg0KRFNNaGlzdCRjb3VudHMNCmBgYA0KDQoNCkxvb2tpbmcgYXQgb3VyIGhpc3RvZ3JhbSwgUiBoYXMgYmlubmVkIG91dCB0aGUgZGF0YSBhcyBmb2xsb3dzOg0KDQozMDAtMzUwbSwgMzUwLTQwMG0sIDQwMC00NTBtDQoNCjEuIFdoaWNoIGVsZXZhdGlvbiBiaW4gYXJlIG1vc3QgcGl4ZWxzIGluPw0KVGhlcmUgYXJlIDY3NTc1IHBpeGVscyBpbiB0aGUgbGFyZ2VzdCBiaW4sIHdoaWNoIGlzIDM1MC00MDBtLg0KDQoNCldlIGNvdWxkIHNwZWNpZnkgZGlmZmVyZW50IGJyZWFrcywgaWYgd2Ugd2lzaGVkIHRvIGhhdmUgYSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9uIG9mIHBpeGVscyBpbiBlYWNoIGJpbi4NCg0KV2UgY2FuIHVzZSB0aG9zZSBiaW5zIHRvIHBsb3Qgb3VyIHJhc3RlciBkYXRhLiBXZSB3aWxsIHVzZSB0aGUgdGVycmFpbi5jb2xvcnMoKSBmdW5jdGlvbiB0byBjcmVhdGUgYSBwYWxldHRlIG9mIDMgY29sb3JzIHRvIHVzZSBpbiBvdXIgcGxvdC4NCg0KVGhlIGJyZWFrcyBhcmd1bWVudCBhbGxvd3MgdXMgdG8gYWRkIGJyZWFrcy4gVG8gc3BlY2lmeSB3aGVyZSB0aGUgYnJlYWtzIG9jY3VyLCB3ZSB1c2UgdGhlIGZvbGxvd2luZyBzeW50YXg6IGJyZWFrcyA9IGModmFsdWUxLCB2YWx1ZTIsIHZhbHVlMykuIFdlIGNhbiBpbmNsdWRlIGFzIGZldyBvciBtYW55IGJyZWFrcyBhcyB3ZSdkIGxpa2UuDQoNCmBgYHtyfQ0KIyBwbG90IHVzaW5nIGJyZWFrcy4NCnBsb3QoRFNNX0hBUlYsIA0KICAgICBicmVha3MgPSBjKDMwMCwgMzUwLCA0MDAsIDQ1MCksIA0KICAgICBjb2wgPSB0ZXJyYWluLmNvbG9ycygzKSwNCiAgICAgbWFpbiA9ICJEaWdpdGFsIFN1cmZhY2UgTW9kZWwgKERTTSlcbiBORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUiKQ0KYGBgDQoNCioqRGF0YSBUaXA6KiogTm90ZSB0aGF0IHdoZW4gd2UgYXNzaWduIGJyZWFrIHZhbHVlcyBhIHNldCBvZiA0IHZhbHVlcyB3aWxsIHJlc3VsdCBpbiAzIGJpbnMgb2YgZGF0YS4NCg0KDQojIyAwMS1DIEZvcm1hdCBQbG90DQoNCklmIHdlIG5lZWQgdG8gY3JlYXRlIG11bHRpcGxlIHBsb3RzIHVzaW5nIHRoZSBzYW1lIGNvbG9yIHBhbGV0dGUsIHdlIGNhbiBjcmVhdGUgYW4gUiBvYmplY3QgKG15Q29sKSBmb3IgdGhlIHNldCBvZiBjb2xvcnMgdGhhdCB3ZSB3YW50IHRvIHVzZS4gV2UgY2FuIHRoZW4gcXVpY2tseSBjaGFuZ2UgdGhlIHBhbGV0dGUgYWNyb3NzIGFsbCBwbG90cyBieSBzaW1wbHkgbW9kaWZ5aW5nIHRoZSBteUNvbCBvYmplY3QuDQoNCldlIGNhbiBsYWJlbCB0aGUgeC0gYW5kIHktYXhlcyBvZiBvdXIgcGxvdCB0b28gdXNpbmcgeGxhYiBhbmQgeWxhYi4NCg0KYGBge3J9DQojIEFzc2lnbiBjb2xvciB0byBhIG9iamVjdCBmb3IgcmVwZWF0IHVzZS8gZWFzZSBvZiBjaGFuZ2luZw0KbXlDb2wgPSB0ZXJyYWluLmNvbG9ycygzKQ0KDQojIEFkZCBheGlzIGxhYmVscw0KcGxvdChEU01fSEFSViwgDQogICAgIGJyZWFrcyA9IGMoMzAwLCAzNTAsIDQwMCwgNDUwKSwgDQogICAgIGNvbCA9IG15Q29sLA0KICAgICBtYWluID0gIkRpZ2l0YWwgU3VyZmFjZSBNb2RlbFxuTkVPTiBIYXJ2YXJkIEZvcmVzdCBGaWVsZCBTaXRlIiwgDQogICAgIHhsYWIgPSAiVVRNIFdlc3RpbmcgQ29vcmRpbmF0ZSAobSkiLCANCiAgICAgeWxhYiA9ICJVVE0gTm9ydGhpbmcgQ29vcmRpbmF0ZSAobSkiKQ0KYGBgDQoNCg0KIyMgMDEtRCBMYXllcmluZyBSYXN0ZXJzDQoNCldlIGNhbiBsYXllciBhIHJhc3RlciBvbiB0b3Agb2YgYSBoaWxsc2hhZGUgcmFzdGVyIGZvciB0aGUgc2FtZSBhcmVhLCBhbmQgdXNlIGEgdHJhbnNwYXJlbmN5IGZhY3RvciB0byBjcmVhdGVkIGEgMy1kaW1lbnNpb25hbCBzaGFkZWQgZWZmZWN0LiBBIGhpbGxzaGFkZSBpcyBhIHJhc3RlciB0aGF0IG1hcHMgdGhlIHNoYWRvd3MgYW5kIHRleHR1cmUgdGhhdCB5b3Ugd291bGQgc2VlIGZyb20gYWJvdmUgd2hlbiB2aWV3aW5nIHRlcnJhaW4uDQoNCmBgYHtyfQ0KIyBpbXBvcnQgRFNNIGhpbGxzaGFkZQ0KRFNNX2hpbGxfSEFSViA8LSByYXN0ZXIoIk5FT04tRFMtQWlyYm9ybmUtUmVtb3RlLVNlbnNpbmcvSEFSVi9EU00vSEFSVl9EU01oaWxsLnRpZiIpDQoNCiMgcGxvdCBoaWxsc2hhZGUgdXNpbmcgYSBncmF5c2NhbGUgY29sb3IgcmFtcCB0aGF0IGxvb2tzIGxpa2Ugc2hhZG93cy4NCnBsb3QoRFNNX2hpbGxfSEFSViwNCiAgICBjb2wgPSBncmV5KDE6MTAwLzEwMCksICAjIGNyZWF0ZSBhIGNvbG9yIHJhbXAgb2YgZ3JleSBjb2xvcnMNCiAgICBsZWdlbmQgPSBGQUxTRSwNCiAgICBtYWluID0gIkhpbGxzaGFkZSAtIERTTVxuIE5FT04gSGFydmFyZCBGb3Jlc3QgRmllbGQgU2l0ZSIsDQogICAgYXhlcyA9IEZBTFNFKQ0KYGBgDQoNCioqRGF0YSBUaXA6KiogVHVybiBvZmYsIG9yIGhpZGUsIHRoZSBsZWdlbmQgb24gYSBwbG90IHVzaW5nIGBsZWdlbmQ9RkFMU0VgLg0KDQpXZSBjYW4gbGF5ZXIgYW5vdGhlciByYXN0ZXIgb24gdG9wIG9mIG91ciBoaWxsc2hhZGUgdXNpbmcgYnkgdXNpbmcgYWRkID0gVFJVRS4gTGV0J3Mgb3ZlcmxheSBEU01fSEFSViBvbiB0b3Agb2YgdGhlIGhpbGxfSEFSVi4NCg0KYGBge3J9DQojIHBsb3QgaGlsbHNoYWRlIHVzaW5nIGEgZ3JheXNjYWxlIGNvbG9yIHJhbXAgdGhhdCBsb29rcyBsaWtlIHNoYWRvd3MuDQpwbG90KERTTV9oaWxsX0hBUlYsDQogICAgY29sID0gZ3JleSgxOjEwMC8xMDApLCAgI2NyZWF0ZSBhIGNvbG9yIHJhbXAgb2YgZ3JleSBjb2xvcnMNCiAgICBsZWdlbmQgPSBGLA0KICAgIG1haW4gPSAiRFNNIHdpdGggSGlsbHNoYWRlIFxuIE5FT04gSGFydmFyZCBGb3Jlc3QgRmllbGQgU2l0ZSIsDQogICAgYXhlcyA9IEZBTFNFKQ0KDQojIGFkZCB0aGUgRFNNIG9uIHRvcCBvZiB0aGUgaGlsbHNoYWRlDQpwbG90KERTTV9IQVJWLA0KICAgICBjb2wgPSByYWluYm93KDEwMCksDQogICAgIGFscGhhID0gMC40LCAjIHBhcnRseSB0cmFuc3BhcmVudA0KICAgICBhZGQgPSBULCAjIGxheWVycyB0aGlzIHBsb3Qgb24gdG9wIG9mIHRoZSBwcmV2aW91cyBwbG90DQogICAgIGxlZ2VuZCA9IEYpDQpgYGANCg0KVGhlIGFscGhhIHZhbHVlIGRldGVybWluZXMgaG93IHRyYW5zcGFyZW50IHRoZSBjb2xvcnMgd2lsbCBiZSAoMCBiZWluZyB0cmFuc3BhcmVudCwgMSBiZWluZyBvcGFxdWUpLiBOb3RlIHRoYXQgaGVyZSB3ZSB1c2VkIHRoZSBjb2xvciBwYWxldHRlIHJhaW5ib3coKSBpbnN0ZWFkIG9mIHRlcnJhaW4uY29sb3IoKS4NCg0KKiBNb3JlIGluZm9ybWF0aW9uIGluIHRoZSBbUiBjb2xvciBwYWxldHRlcyBkb2N1bWVudGF0aW9uXShodHRwczovL3N0YXQuZXRoei5jaC9SLW1hbnVhbC9SLWRldmVsL2xpYnJhcnkvZ3JEZXZpY2VzL2h0bWwvcGFsZXR0ZXMuaHRtbCkuDQoNCg0KLS0tLQ0KDQojIFJhc3RlciAwMjogV2hlbiBSYXN0ZXJzIERvbid0IExpbmUgVXAgLSBSZXByb2plY3QgUmFzdGVyIERhdGEgaW4gUg0KDQpPcmlnaW5hbCBBdXRob3JzOiBMZWFoIEEuIFdhc3NlciwgTWVnYW4gQS4gSm9uZXMsIFphY2sgQnJ5bSwgS3Jpc3RpbmEgUmllbWVyLCBKYXNvbiBXaWxsaWFtcywgSmVmZiBIb2xsaXN0ZXIsIE1pa2UgU21vcnVsDQoNCk9yaWdpbmFsIExhc3QgVXBkYXRlZDogQXByIDgsIDIwMjENCg0KU29tZXRpbWVzIHdlIGVuY291bnRlciByYXN0ZXIgZGF0YXNldHMgdGhhdCBkbyBub3QgImxpbmUgdXAiIHdoZW4gcGxvdHRlZCBvciBhbmFseXplZC4gUmFzdGVycyB0aGF0IGRvbid0IGxpbmUgdXAgYXJlIG1vc3Qgb2Z0ZW4gaW4gZGlmZmVyZW50IENvb3JkaW5hdGUgUmVmZXJlbmNlIFN5c3RlbXMgKENSUykuDQoNClRoaXMgdHV0b3JpYWwgZXhwbGFpbnMgaG93IHRvIGRlYWwgd2l0aCByYXN0ZXJzIGluIGRpZmZlcmVudCwga25vd24gQ1JTcy4gSXQgd2lsbCB3YWxrIHRob3VnaCByZXByb2plY3RpbmcgcmFzdGVycyBpbiBSIHVzaW5nIHRoZSBwcm9qZWN0UmFzdGVyKCkgZnVuY3Rpb24gaW4gdGhlIHJhc3RlciBwYWNrYWdlLg0KDQojIyAwMi1BIFJhc3RlciBQcm9qZWN0aW9uIGluIFINCg0KSW4gdGhlIFBsb3QgUmFzdGVyIERhdGEgaW4gUiB0dXRvcmlhbCwgd2UgbGVhcm5lZCBob3cgdG8gbGF5ZXIgYSByYXN0ZXIgZmlsZSBvbiB0b3Agb2YgYSBoaWxsc2hhZGUgZm9yIGEgbmljZSBsb29raW5nIGJhc2VtYXAuIEluIHRoYXQgdHV0b3JpYWwsIGFsbCBvZiBvdXIgZGF0YSB3ZXJlIGluIHRoZSBzYW1lIENSUy4gV2hhdCBoYXBwZW5zIHdoZW4gdGhpbmdzIGRvbid0IGxpbmUgdXA/DQoNCkxldCdzIGNyZWF0ZSBhIG1hcCBvZiB0aGUgSGFydmFyZCBGb3Jlc3QgRGlnaXRhbCBUZXJyYWluIE1vZGVsIChEVE1fSEFSVikgZHJhcGVkIG9yIGxheWVyZWQgb24gdG9wIG9mIHRoZSBoaWxsc2hhZGUgKERUTV9oaWxsX0hBUlYpLg0KDQpgYGB7cn0NCkRUTV9IQVJWIDwtIHJhc3RlcigiTkVPTi1EUy1BaXJib3JuZS1SZW1vdGUtU2Vuc2luZy9IQVJWL0RUTS9IQVJWX2R0bUNyb3AudGlmIikNCkRUTV9oaWxsX0hBUlYgPC0gcmFzdGVyKCJORU9OLURTLUFpcmJvcm5lLVJlbW90ZS1TZW5zaW5nL0hBUlYvRFRNL0hBUlZfRFRNaGlsbF9XR1M4NC50aWYiKQ0KDQojIHBsb3QgaGlsbHNoYWRlIHVzaW5nIGEgZ3JheXNjYWxlIGNvbG9yIHJhbXAgDQojcGxvdChEVE1faGlsbF9IQVJWLA0KICAgICNjb2wgPSBncmV5KDE6MTAwLzEwMCksDQogICAgI2xlZ2VuZCA9IEZBTFNFLA0KICAgICNtYWluID0gIkRUTSBIaWxsc2hhZGVcbiBORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUiKQ0KDQojIG92ZXJsYXkgdGhlIERUTSBvbiB0b3Agb2YgdGhlIGhpbGxzaGFkZQ0KI3Bsb3QoRFRNX0hBUlYsDQogICAgICNjb2wgPSB0ZXJyYWluLmNvbG9ycygxMCksDQogICAgICNhbHBoYSA9IDAuNCwNCiAgICAgI2FkZCA9IFRSVUUsDQogICAgICNsZWdlbmQgPSBGQUxTRSkNCg0KIyBJIHJhbiB0aGlzIGNodW5rIHR3aWNlIGFuZCBpdCBjcmFzaGVkIG15IFIgc2Vzc2lvbiBib3RoIHRpbWVzIChtaWdodCBiZSBhIFJBTSBlcnJvcikNCiMgSXQncyBzdXBwb3NlZCB0byBvbmx5IHBsb3QgdGhlIGZpcnN0IG9uZQ0KYGBgDQoNCk91ciByZXN1bHRzIGFyZSBjdXJpb3VzIC0gdGhlIERpZ2l0YWwgVGVycmFpbiBNb2RlbCAoRFRNX0hBUlYpIGRpZCBub3QgcGxvdCBvbiB0b3Agb2Ygb3VyIGhpbGxzaGFkZS4gVGhlIGhpbGxzaGFkZSBwbG90dGVkIGp1c3QgZmluZSBvbiBpdCdzIG93bi4gTGV0J3MgdHJ5IHRvIHBsb3QgdGhlIERUTSBvbiBpdCdzIG93biB0byBtYWtlIHN1cmUgdGhlcmUgYXJlIGRhdGEgdGhlcmUuDQoNCioqQ29kZSBUaXA6KiogRm9yIGJvb2xlYW4gUiBlbGVtZW50cywgc3VjaCBhcyBgYWRkID0gVFJVRWAsIHlvdSBjYW4gdXNlIGBUYCBhbmQgYEZgIGluIHBsYWNlIG9mIGBUUlVFYCBhbmQgYEZBTFNFYA0KDQpgYGB7cn0NCiMgUGxvdCBEVE0gDQpwbG90KERUTV9IQVJWLA0KICAgICBjb2wgPSB0ZXJyYWluLmNvbG9ycygxMCksDQogICAgIGFscGhhID0gMSwNCiAgICAgbGVnZW5kID0gRiwNCiAgICAgbWFpbiA9ICJEaWdpdGFsIFRlcnJhaW4gTW9kZWxcbiBORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUiKQ0KYGBgDQoNCk91ciBEVE0gc2VlbXMgdG8gY29udGFpbiBkYXRhIGFuZCBwbG90cyBqdXN0IGZpbmUuIExldCdzIG5leHQgY2hlY2sgdGhlIENvb3JkaW5hdGUgUmVmZXJlbmNlIFN5c3RlbSAoQ1JTKSBhbmQgY29tcGFyZSBpdCB0byBvdXIgaGlsbHNoYWRlLg0KDQpgYGB7cn0NCiMgdmlldyBjcnMgZm9yIERUTQ0KY3JzKERUTV9IQVJWKQ0KYGBgDQoNCmBgYHtyfQ0KIyB2aWV3IGNycyBmb3IgaGlsbHNoYWRlDQpjcnMoRFRNX2hpbGxfSEFSVikNCmBgYA0KDQpBaGEhIERUTV9IQVJWIGlzIGluIHRoZSBVVE0gcHJvamVjdGlvbi4gRFRNX2hpbGxfSEFSViBpcyBpbiBHZW9ncmFwaGljIFdHUzg0IC0gd2hpY2ggaXMgcmVwcmVzZW50ZWQgYnkgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB2YWx1ZXMuIEJlY2F1c2UgdGhlIHR3byByYXN0ZXJzIGFyZSBpbiBkaWZmZXJlbnQgQ1JTcywgdGhleSBkb24ndCBsaW5lIHVwIHdoZW4gcGxvdHRlZCBpbiBSLiBXZSBuZWVkIHRvIHJlcHJvamVjdCBEVE1faGlsbF9IQVJWIGludG8gdGhlIFVUTSBDUlMuIEFsdGVybmF0aXZlbHksIHdlIGNvdWxkIHByb2plY3QgRFRNX0hBUlYgaW50byBXR1M4NC4NCg0KDQojIyAwMi1CIFJlcHJvamVjdCBSYXN0ZXJzDQoNCldlIGNhbiB1c2UgdGhlIHByb2plY3RSYXN0ZXIgZnVuY3Rpb24gdG8gcmVwcm9qZWN0IGEgcmFzdGVyIGludG8gYSBuZXcgQ1JTLiBLZWVwIGluIG1pbmQgdGhhdCByZXByb2plY3Rpb24gb25seSB3b3JrcyB3aGVuIHlvdSBmaXJzdCBoYXZlIGEgZGVmaW5lZCBDUlMgZm9yIHRoZSByYXN0ZXIgb2JqZWN0IHRoYXQgeW91IHdhbnQgdG8gcmVwcm9qZWN0LiBJdCBjYW5ub3QgYmUgdXNlZCBpZiBubyBDUlMgaXMgZGVmaW5lZC4gTHVja3kgZm9yIHVzLCB0aGUgRFRNX2hpbGxfSEFSViBoYXMgYSBkZWZpbmVkIENSUy4NCg0KKipEYXRhIFRpcDoqKiBXaGVuIHdlIHJlcHJvamVjdCBhIHJhc3Rlciwgd2UgbW92ZSBpdCBmcm9tIG9uZSAiZ3JpZCIgdG8gYW5vdGhlci4gVGh1cywgd2UgYXJlIG1vZGlmeWluZyB0aGUgZGF0YSEgS2VlcCB0aGlzIGluIG1pbmQgYXMgd2Ugd29yayB3aXRoIHJhc3RlciBkYXRhLg0KVG8gdXNlIHRoZSBwcm9qZWN0UmFzdGVyIGZ1bmN0aW9uLCB3ZSBuZWVkIHRvIGRlZmluZSB0d28gdGhpbmdzOg0KDQoqIHRoZSBvYmplY3Qgd2Ugd2FudCB0byByZXByb2plY3QgYW5kDQoqIHRoZSBDUlMgdGhhdCB3ZSB3YW50IHRvIHJlcHJvamVjdCBpdCB0by4NCg0KVGhlIHN5bnRheCBpcyBwcm9qZWN0UmFzdGVyKFJhc3Rlck9iamVjdCwgY3JzID0gQ1JTVG9SZXByb2plY3RUbykNCg0KV2Ugd2FudCB0aGUgQ1JTIG9mIG91ciBoaWxsc2hhZGUgdG8gbWF0Y2ggdGhlIERUTV9IQVJWIHJhc3Rlci4gV2UgY2FuIHRodXMgYXNzaWduIHRoZSBDUlMgb2Ygb3VyIERUTV9IQVJWIHRvIG91ciBoaWxsc2hhZGUgd2l0aGluIHRoZSBwcm9qZWN0UmFzdGVyKCkgZnVuY3Rpb24gYXMgZm9sbG93czogY3JzPWNycyhEVE1fSEFSVikNCg0KYGBge3J9DQojIHJlcHJvamVjdCB0byBVVE0NCkRUTV9oaWxsX1VUTVoxOE5fSEFSViA8LSBwcm9qZWN0UmFzdGVyKERUTV9oaWxsX0hBUlYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JzID0gY3JzKERUTV9IQVJWKSkNCg0KIyBjb21wYXJlIGF0dHJpYnV0ZXMgb2YgRFRNX2hpbGxfVVRNWjE4TiB0byBEVE1faGlsbA0KY3JzKERUTV9oaWxsX1VUTVoxOE5fSEFSVikNCmBgYA0KDQpgYGB7cn0NCiMgY29tcGFyZSBhdHRyaWJ1dGVzIG9mIERUTV9oaWxsX1VUTVoxOE4gdG8gRFRNX2hpbGwNCmNycyhEVE1faGlsbF9IQVJWKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjb21wYXJlIGF0dHJpYnV0ZXMgb2YgRFRNX2hpbGxfVVRNWjE4TiB0byBEVE1faGlsbA0KZXh0ZW50KERUTV9oaWxsX1VUTVoxOE5fSEFSVikNCg0KZXh0ZW50KERUTV9oaWxsX0hBUlYpDQpgYGANCg0KTm90aWNlIGluIHRoZSBvdXRwdXQgYWJvdmUgdGhhdCB0aGUgY3JzKCkgb2YgRFRNX2hpbGxfVVRNWjE4Tl9IQVJWIGlzIG5vdyBVVE0uIEFsc28sIHRoZSBleHRlbnQgdmFsdWVzIG9mIERUTV9oaWxsVVRNWjE4Tl9IQVJWIGFyZSBkaWZmZXJlbnQgZnJvbSBEVE1faGlsbF9IQVJWLg0KDQoNCiMjIENoYWxsZW5nZTogRXh0ZW50IENoYW5nZSB3aXRoIENSUyBDaGFuZ2UgDQoNCjEuIFdoeSBkbyB5b3UgdGhpbmsgdGhlIHR3byBleHRlbnRzIChvZiBEVE1faGlsbFVUTVoxOE5fSEFSViBhbmQgRFRNX2hpbGxfSEFSVikgZGlmZmVyPw0KV2hpbGUgRFRNX2hpbGxfVVRNWjE4Tl9IQVJWIGlzIGluIFVUTSAoaS5lLiBpbiB0ZXJtcyBvZiBtZXRlcnMpLCBEVE1faGlsbF9IQVJWIGlzIGluIHRlcm1zIG9mIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuIEluIHRoZSBjb252ZXJzaW9uIGZyb20gb25lIHByb2plY3Rpb24gdG8gYW5vdGhlciwgdGhlIHVuaXRzIGNoYW5nZSwgc28gdGhlIGV4dGVudHMgY2hhbmdlIHRvby4NCg0KDQojIyAwMi1DIERlYWwgd2l0aCBSYXN0ZXIgUmVzb2x1dGlvbg0KDQpMZXQncyBuZXh0IGhhdmUgYSBsb29rIGF0IHRoZSByZXNvbHV0aW9uIG9mIG91ciByZXByb2plY3RlZCBoaWxsc2hhZGUuDQoNCmBgYHtyfQ0KIyBjb21wYXJlIHJlc29sdXRpb24NCnJlcyhEVE1faGlsbF9VVE1aMThOX0hBUlYpDQpgYGANCg0KVGhlIG91dHB1dCByZXNvbHV0aW9uIG9mIERUTV9oaWxsX1VUTVoxOE5fSEFSViBpcyAxIHggMC45OTguIFlldCwgd2Uga25vdyB0aGF0IHRoZSByZXNvbHV0aW9uIGZvciB0aGUgZGF0YSBzaG91bGQgYmUgMW0geCAxbS4gV2UgY2FuIHRlbGwgUiB0byBmb3JjZSBvdXIgbmV3bHkgcmVwcm9qZWN0ZWQgcmFzdGVyIHRvIGJlIDFtIHggMW0gcmVzb2x1dGlvbiBieSBhZGRpbmcgYSBsaW5lIG9mIGNvZGUgKHJlcz0pLg0KDQpgYGB7cn0NCiMgYWRqdXN0IHRoZSByZXNvbHV0aW9uIA0KRFRNX2hpbGxfVVRNWjE4Tl9IQVJWIDwtIHByb2plY3RSYXN0ZXIoRFRNX2hpbGxfSEFSViwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3JzID0gY3JzKERUTV9IQVJWKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXMgPSAxKQ0KDQojIHZpZXcgcmVzb2x1dGlvbg0KcmVzKERUTV9oaWxsX1VUTVoxOE5fSEFSVikNCmBgYA0KDQpMZXQncyBwbG90IG91ciBuZXdseSByZXByb2plY3RlZCByYXN0ZXIuDQoNCmBgYHtyfQ0KIyBwbG90IG5ld2x5IHJlcHJvamVjdGVkIGhpbGxzaGFkZQ0KcGxvdChEVE1faGlsbF9VVE1aMThOX0hBUlYsDQogICAgY29sID0gZ3JleSgxOjEwMC8xMDApLA0KICAgIGxlZ2VuZCA9IEYsDQogICAgbWFpbiA9ICJEVE0gd2l0aCBIaWxsc2hhZGVcbiBORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUiKQ0KDQojIG92ZXJsYXkgdGhlIERUTSBvbiB0b3Agb2YgdGhlIGhpbGxzaGFkZQ0KcGxvdChEVE1fSEFSViwNCiAgICAgY29sID0gdGVycmFpbi5jb2xvcnMoMTAwKSwNCiAgICAgYWxwaGEgPSAwLjQsDQogICAgIGFkZCA9IFQsDQogICAgIGxlZ2VuZCA9IEYpDQpgYGANCg0KV2UgaGF2ZSBub3cgc3VjY2Vzc2Z1bGx5IGRyYXBlZCB0aGUgRGlnaXRhbCBUZXJyYWluIE1vZGVsIG9uIHRvcCBvZiBvdXIgaGlsbHNoYWRlIHRvIHByb2R1Y2UgYSBuaWNlIGxvb2tpbmcsIHRleHR1cmVkIG1hcCENCg0KDQojIyBDaGFsbGVuZ2U6IFJlcHJvamVjdCBhIERpZ2l0YWwgU3VyZmFjZSBNb2RlbCBDcmVhdGUgYSBtYXAgb2YgdGhlIFNhbiBKb2FxdWluIEV4cGVyaW1lbnRhbCBSYW5nZSBmaWVsZCBzaXRlDQoNClVzZSB0aGUgYE5FT04tRFMtQWlyYm9ybmUtUmVtb3RlLVNlbnNpbmcvU0pFUi9EU00vU0pFUl9kc21Dcm9wLnRpZmAgYW5kIGBORU9OLURTLUFpcmJvcm5lLVJlbW90ZS1TZW5zaW5nL1NKRVIvRFNNL1NKRVJfRFNNaGlsbF9XR1M4NC50aWZgIGZpbGVzLg0KDQpXZSdsbCBsb2FkIGluIHRoZSB0d28gcmFzdGVyTGF5ZXJzIGZyb20gdGhlIGZpbGVzIGxpc3RlZCBhYm92ZToNCg0KYGBge3J9DQpEU01fU0pFUiA8LSByYXN0ZXIoIk5FT04tRFMtQWlyYm9ybmUtUmVtb3RlLVNlbnNpbmcvU0pFUi9EU00vU0pFUl9kc21Dcm9wLnRpZiIpDQpEU01faGlsbF9VVE1aMThOX1NKRVIgPC0gcmFzdGVyKCJORU9OLURTLUFpcmJvcm5lLVJlbW90ZS1TZW5zaW5nL1NKRVIvRFNNL1NKRVJfRFNNaGlsbF9XR1M4NC50aWYiKQ0KYGBgDQoNCjEuIFVzZSB0aGUgY3JzKCkgZnVuY3Rpb24gdG8gbG9vayBhdCB0aGVpciBjb29yZGluYXRlIHJlZmVyZW5jZSBzeXN0ZW1zIChmb2N1cyBvbiB0aGUgcHJvaiBzdHJpbmcgYXQgdGhlIHRvcCwgbm90IHRoZSBsb25nIHdhcm5pbmcgbWVzc2FnZSk6DQoNCmBgYHtyfQ0KY3JzKERTTV9TSkVSKSAjIHByb2o9dXRtDQpjcnMoRFNNX2hpbGxfVVRNWjE4Tl9TSkVSKSAjIHByb2o9bG9uZ2xhdA0KYGBgDQoNCjIuIFJlcHJvamVjdCBvbmUgb2YgdGhlbSBzbyB0aGV5IGhhdmUgdGhlIHNhbWUgQ1JTOg0KDQpgYGB7cn0NCkRTTV9oaWxsX1NKRVJfVVRNIDwtIHByb2plY3RSYXN0ZXIoRFNNX2hpbGxfVVRNWjE4Tl9TSkVSLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcnMgPSBjcnMoRFNNX1NKRVIpKQ0KYGBgDQoNCjMuIENoZWNrIHlvdXIgd29yayB1c2luZyB0aGUgY3JzKCkgZnVuY3Rpb246DQoNCmBgYHtyfQ0KY3JzKERTTV9oaWxsX1NKRVJfVVRNKQ0KYGBgDQoNCkdyZWF0IHdvcmshIFlvdSBoYXZlIGxlYXJuZWQgYSBsb3QgYWJvdXQgd29ya2luZyB3aXRoIHJhc3RlciBkYXRhIGFuZCBwbG90dGluZyB1c2luZyBiYXNlIFIu